blob: 2f0cc4702121782bbd22abf657aa5dd6f6915a4a [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
Ed Tanousd093c992023-01-19 19:01:49 -080031#include <boost/algorithm/string/case_conv.hpp>
George Liue99073f2022-12-09 11:06:16 +080032#include <boost/system/error_code.hpp>
Ed Tanousef4c65b2023-04-24 15:28:50 -070033#include <boost/url/format.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070034#include <sdbusplus/asio/property.hpp>
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080035#include <sdbusplus/bus/match.hpp>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020036#include <sdbusplus/unpack_properties.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050037
George Liu2b731192023-01-11 16:27:13 +080038#include <array>
George Liu0ed80c82020-05-12 16:06:27 +080039#include <filesystem>
George Liu2b731192023-01-11 16:27:13 +080040#include <string_view>
41
Ed Tanous1abe55e2018-09-05 08:30:59 -070042namespace redfish
43{
Ed Tanous27826b52018-10-29 11:40:58 -070044
Andrew Geissler0e7de462019-03-04 19:11:54 -060045// Match signals added on software path
Ed Tanouscf9e4172022-12-21 09:30:16 -080046// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050047static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
Ed Tanouscf9e4172022-12-21 09:30:16 -080048// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050049static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher;
Andrew Geissler0e7de462019-03-04 19:11:54 -060050// Only allow one update at a time
Ed Tanouscf9e4172022-12-21 09:30:16 -080051// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Andrew Geissler0e7de462019-03-04 19:11:54 -060052static bool fwUpdateInProgress = false;
Andrew Geissler86adcd62019-04-18 10:58:05 -050053// Timer for software available
Ed Tanouscf9e4172022-12-21 09:30:16 -080054// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Ed Tanous271584a2019-07-09 16:24:22 -070055static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
Andrew Geissler86adcd62019-04-18 10:58:05 -050056
John Edward Broadbent7e860f12021-04-08 15:57:16 -070057inline static void cleanUp()
Andrew Geissler86adcd62019-04-18 10:58:05 -050058{
59 fwUpdateInProgress = false;
60 fwUpdateMatcher = nullptr;
James Feist4cde5d92020-06-11 10:39:55 -070061 fwUpdateErrorMatcher = nullptr;
Andrew Geissler86adcd62019-04-18 10:58:05 -050062}
John Edward Broadbent7e860f12021-04-08 15:57:16 -070063inline static void activateImage(const std::string& objPath,
64 const std::string& service)
Andrew Geissler86adcd62019-04-18 10:58:05 -050065{
Ed Tanous62598e32023-07-17 17:06:25 -070066 BMCWEB_LOG_DEBUG("Activate image for {} {}", objPath, service);
George Liu9ae226f2023-06-21 17:56:46 +080067 sdbusplus::asio::setProperty(
68 *crow::connections::systemBus, service, objPath,
69 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
70 "xyz.openbmc_project.Software.Activation.RequestedActivations.Active",
Ed Tanous8b242752023-06-27 17:17:13 -070071 [](const boost::system::error_code& ec) {
72 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -070073 {
Ed Tanous62598e32023-07-17 17:06:25 -070074 BMCWEB_LOG_DEBUG("error_code = {}", ec);
75 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
Ed Tanous002d39b2022-05-31 08:59:27 -070076 }
Patrick Williams5a39f772023-10-20 11:20:21 -050077 });
Andrew Geissler86adcd62019-04-18 10:58:05 -050078}
Andrew Geissler0554c982019-04-23 14:40:12 -050079
80// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
81// then no asyncResp updates will occur
zhanghch058d1b46d2021-04-01 11:18:24 +080082static void
83 softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Patrick Williams59d494e2022-07-22 19:26:55 -050084 sdbusplus::message_t& m, task::Payload&& payload)
Andrew Geissler86adcd62019-04-18 10:58:05 -050085{
Michael Shen80f79a42023-08-24 13:41:53 +000086 dbus::utility::DBusInterfacesMap interfacesProperties;
Andrew Geissler86adcd62019-04-18 10:58:05 -050087
88 sdbusplus::message::object_path objPath;
89
90 m.read(objPath, interfacesProperties);
91
Ed Tanous62598e32023-07-17 17:06:25 -070092 BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
Ed Tanouse3eb3d62022-12-21 11:56:07 -080093 for (const auto& interface : interfacesProperties)
Andrew Geissler86adcd62019-04-18 10:58:05 -050094 {
Ed Tanous62598e32023-07-17 17:06:25 -070095 BMCWEB_LOG_DEBUG("interface = {}", interface.first);
Andrew Geissler86adcd62019-04-18 10:58:05 -050096
97 if (interface.first == "xyz.openbmc_project.Software.Activation")
98 {
Andrew Geissler86adcd62019-04-18 10:58:05 -050099 // Retrieve service and activate
George Liu2b731192023-01-11 16:27:13 +0800100 constexpr std::array<std::string_view, 1> interfaces = {
101 "xyz.openbmc_project.Software.Activation"};
102 dbus::utility::getDbusObject(
103 objPath.str, interfaces,
Ed Tanousa3e65892021-09-16 14:13:20 -0700104 [objPath, asyncResp, payload(std::move(payload))](
Ed Tanous8b242752023-06-27 17:17:13 -0700105 const boost::system::error_code& ec,
Ed Tanousa3e65892021-09-16 14:13:20 -0700106 const std::vector<
107 std::pair<std::string, std::vector<std::string>>>&
108 objInfo) mutable {
Ed Tanous8b242752023-06-27 17:17:13 -0700109 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -0700110 {
Ed Tanous62598e32023-07-17 17:06:25 -0700111 BMCWEB_LOG_DEBUG("error_code = {}", ec);
112 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
Andrew Geissler0554c982019-04-23 14:40:12 -0500113 if (asyncResp)
114 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700115 messages::internalError(asyncResp->res);
116 }
117 cleanUp();
118 return;
119 }
120 // Ensure we only got one service back
121 if (objInfo.size() != 1)
122 {
Ed Tanous62598e32023-07-17 17:06:25 -0700123 BMCWEB_LOG_ERROR("Invalid Object Size {}", objInfo.size());
Ed Tanous002d39b2022-05-31 08:59:27 -0700124 if (asyncResp)
125 {
126 messages::internalError(asyncResp->res);
127 }
128 cleanUp();
129 return;
130 }
131 // cancel timer only when
132 // xyz.openbmc_project.Software.Activation interface
133 // is added
134 fwAvailableTimer = nullptr;
135
136 activateImage(objPath.str, objInfo[0].first);
137 if (asyncResp)
138 {
139 std::shared_ptr<task::TaskData> task =
140 task::TaskData::createTask(
Ed Tanous8b242752023-06-27 17:17:13 -0700141 [](const boost::system::error_code& ec2,
Patrick Williams59d494e2022-07-22 19:26:55 -0500142 sdbusplus::message_t& msg,
Ed Tanous002d39b2022-05-31 08:59:27 -0700143 const std::shared_ptr<task::TaskData>&
144 taskData) {
Ed Tanous8b242752023-06-27 17:17:13 -0700145 if (ec2)
Ed Tanous002d39b2022-05-31 08:59:27 -0700146 {
147 return task::completed;
148 }
149
150 std::string iface;
151 dbus::utility::DBusPropertiesMap values;
152
153 std::string index = std::to_string(taskData->index);
154 msg.read(iface, values);
155
156 if (iface == "xyz.openbmc_project.Software.Activation")
157 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000158 const std::string* state = nullptr;
Ed Tanous002d39b2022-05-31 08:59:27 -0700159 for (const auto& property : values)
160 {
161 if (property.first == "Activation")
162 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000163 state = std::get_if<std::string>(
164 &property.second);
165 if (state == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700166 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700167 taskData->messages.emplace_back(
168 messages::internalError());
James Feist32898ce2020-03-10 16:16:52 -0700169 return task::completed;
170 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700171 }
172 }
James Feist32898ce2020-03-10 16:16:52 -0700173
Ed Tanous002d39b2022-05-31 08:59:27 -0700174 if (state == nullptr)
175 {
176 return !task::completed;
177 }
James Feist32898ce2020-03-10 16:16:52 -0700178
Ed Tanous11ba3972022-07-11 09:50:41 -0700179 if (state->ends_with("Invalid") ||
180 state->ends_with("Failed"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700181 {
182 taskData->state = "Exception";
183 taskData->status = "Warning";
184 taskData->messages.emplace_back(
185 messages::taskAborted(index));
186 return task::completed;
187 }
James Feiste5d50062020-05-11 17:29:00 -0700188
Ed Tanous11ba3972022-07-11 09:50:41 -0700189 if (state->ends_with("Staged"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700190 {
191 taskData->state = "Stopping";
192 taskData->messages.emplace_back(
193 messages::taskPaused(index));
194
195 // its staged, set a long timer to
196 // allow them time to complete the
197 // update (probably cycle the
198 // system) if this expires then
199 // task will be cancelled
200 taskData->extendTimer(std::chrono::hours(5));
201 return !task::completed;
202 }
203
Ed Tanous11ba3972022-07-11 09:50:41 -0700204 if (state->ends_with("Active"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700205 {
206 taskData->messages.emplace_back(
207 messages::taskCompletedOK(index));
208 taskData->state = "Completed";
209 return task::completed;
210 }
211 }
212 else if (
213 iface ==
214 "xyz.openbmc_project.Software.ActivationProgress")
215 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700216 const uint8_t* progress = nullptr;
217 for (const auto& property : values)
218 {
219 if (property.first == "Progress")
220 {
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000221 progress =
222 std::get_if<uint8_t>(&property.second);
223 if (progress == nullptr)
James Feist32898ce2020-03-10 16:16:52 -0700224 {
James Feist32898ce2020-03-10 16:16:52 -0700225 taskData->messages.emplace_back(
Ed Tanous002d39b2022-05-31 08:59:27 -0700226 messages::internalError());
227 return task::completed;
James Feist32898ce2020-03-10 16:16:52 -0700228 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700229 }
230 }
James Feist32898ce2020-03-10 16:16:52 -0700231
Ed Tanous002d39b2022-05-31 08:59:27 -0700232 if (progress == nullptr)
233 {
234 return !task::completed;
235 }
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000236 taskData->percentComplete = *progress;
Ed Tanous002d39b2022-05-31 08:59:27 -0700237 taskData->messages.emplace_back(
Gayathri Leburu0fb5b502022-11-17 04:51:55 +0000238 messages::taskProgressChanged(index,
239 *progress));
James Feist32898ce2020-03-10 16:16:52 -0700240
Ed Tanous002d39b2022-05-31 08:59:27 -0700241 // if we're getting status updates it's
242 // still alive, update timer
243 taskData->extendTimer(std::chrono::minutes(5));
244 }
245
246 // as firmware update often results in a
247 // reboot, the task may never "complete"
248 // unless it is an error
249
250 return !task::completed;
Patrick Williams5a39f772023-10-20 11:20:21 -0500251 },
Ed Tanous002d39b2022-05-31 08:59:27 -0700252 "type='signal',interface='org.freedesktop.DBus.Properties',"
253 "member='PropertiesChanged',path='" +
254 objPath.str + "'");
255 task->startTimer(std::chrono::minutes(5));
256 task->populateResp(asyncResp->res);
257 task->payload.emplace(std::move(payload));
258 }
259 fwUpdateInProgress = false;
Patrick Williams5a39f772023-10-20 11:20:21 -0500260 });
Patrick Williams62bafc02022-09-08 17:35:35 -0500261
262 break;
Andrew Geissler86adcd62019-04-18 10:58:05 -0500263 }
264 }
265}
266
Myung Bae8549b952023-08-16 15:18:19 -0400267inline void afterAvailbleTimerAsyncWait(
268 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
269 const boost::system::error_code& ec)
270{
271 cleanUp();
272 if (ec == boost::asio::error::operation_aborted)
273 {
274 // expected, we were canceled before the timer completed.
275 return;
276 }
277 BMCWEB_LOG_ERROR("Timed out waiting for firmware object being created");
278 BMCWEB_LOG_ERROR("FW image may has already been uploaded to server");
279 if (ec)
280 {
281 BMCWEB_LOG_ERROR("Async_wait failed{}", ec);
282 return;
283 }
284 if (asyncResp)
285 {
286 redfish::messages::internalError(asyncResp->res);
287 }
288}
289
290inline void
291 handleUpdateErrorType(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
292 const std::string& url, const std::string& type)
293{
294 if (type == "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
295 {
296 redfish::messages::invalidUpload(asyncResp->res, url,
297 "Invalid archive");
298 }
299 else if (type ==
300 "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure")
301 {
302 redfish::messages::invalidUpload(asyncResp->res, url,
303 "Invalid manifest");
304 }
305 else if (type == "xyz.openbmc_project.Software.Image.Error.ImageFailure")
306 {
307 redfish::messages::invalidUpload(asyncResp->res, url,
308 "Invalid image format");
309 }
310 else if (type == "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
311 {
312 redfish::messages::invalidUpload(asyncResp->res, url,
313 "Image version already exists");
314
315 redfish::messages::resourceAlreadyExists(
316 asyncResp->res, "UpdateService", "Version", "uploaded version");
317 }
318 else if (type == "xyz.openbmc_project.Software.Image.Error.BusyFailure")
319 {
320 redfish::messages::resourceExhaustion(asyncResp->res, url);
321 }
Myung Bae4034a652023-08-17 08:47:35 -0400322 else if (type == "xyz.openbmc_project.Software.Version.Error.Incompatible")
Myung Bae8549b952023-08-16 15:18:19 -0400323 {
Myung Bae4034a652023-08-17 08:47:35 -0400324 redfish::messages::invalidUpload(asyncResp->res, url,
325 "Incompatible image version");
326 }
327 else if (type ==
328 "xyz.openbmc_project.Software.Version.Error.ExpiredAccessKey")
329 {
330 redfish::messages::invalidUpload(asyncResp->res, url,
331 "Update Access Key Expired");
332 }
333 else if (type ==
334 "xyz.openbmc_project.Software.Version.Error.InvalidSignature")
335 {
336 redfish::messages::invalidUpload(asyncResp->res, url,
337 "Invalid image signature");
338 }
339 else if (type ==
340 "xyz.openbmc_project.Software.Image.Error.InternalFailure" ||
341 type == "xyz.openbmc_project.Software.Version.Error.HostFile")
342 {
343 BMCWEB_LOG_ERROR("Software Image Error type={}", type);
Myung Bae8549b952023-08-16 15:18:19 -0400344 redfish::messages::internalError(asyncResp->res);
345 }
Myung Bae4034a652023-08-17 08:47:35 -0400346 else
347 {
348 // Unrelated error types. Ignored
349 BMCWEB_LOG_INFO("Non-Software-related Error type={}. Ignored", type);
350 return;
351 }
352 // Clear the timer
353 fwAvailableTimer = nullptr;
Myung Bae8549b952023-08-16 15:18:19 -0400354}
355
356inline void
357 afterUpdateErrorMatcher(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
358 const std::string& url, sdbusplus::message_t& m)
359{
Michael Shen80f79a42023-08-24 13:41:53 +0000360 dbus::utility::DBusInterfacesMap interfacesProperties;
Myung Bae8549b952023-08-16 15:18:19 -0400361 sdbusplus::message::object_path objPath;
362 m.read(objPath, interfacesProperties);
363 BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
364 for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
365 interface : interfacesProperties)
366 {
367 if (interface.first == "xyz.openbmc_project.Logging.Entry")
368 {
369 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
370 value : interface.second)
371 {
372 if (value.first != "Message")
373 {
374 continue;
375 }
376 const std::string* type =
377 std::get_if<std::string>(&value.second);
378 if (type == nullptr)
379 {
380 // if this was our message, timeout will cover it
381 return;
382 }
Myung Bae8549b952023-08-16 15:18:19 -0400383 handleUpdateErrorType(asyncResp, url, *type);
384 }
385 }
386 }
387}
388
Andrew Geissler0554c982019-04-23 14:40:12 -0500389// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
390// then no asyncResp updates will occur
Ed Tanousb5a76932020-09-29 16:16:58 -0700391static void monitorForSoftwareAvailable(
zhanghch058d1b46d2021-04-01 11:18:24 +0800392 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
393 const crow::Request& req, const std::string& url,
Gunnar Mills5d138942022-09-07 10:26:21 -0500394 int timeoutTimeSeconds = 25)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500395{
396 // Only allow one FW update at a time
Ed Tanouse05aec52022-01-25 10:28:56 -0800397 if (fwUpdateInProgress)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500398 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500399 if (asyncResp)
400 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500401 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
402 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500403 return;
404 }
405
Andrew Geissler0554c982019-04-23 14:40:12 -0500406 fwAvailableTimer =
Ed Tanous271584a2019-07-09 16:24:22 -0700407 std::make_unique<boost::asio::steady_timer>(*req.ioService);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500408
Ed Tanous271584a2019-07-09 16:24:22 -0700409 fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500410
411 fwAvailableTimer->async_wait(
Myung Bae8549b952023-08-16 15:18:19 -0400412 std::bind_front(afterAvailbleTimerAsyncWait, asyncResp));
413
Ed Tanousa3e65892021-09-16 14:13:20 -0700414 task::Payload payload(req);
Patrick Williams59d494e2022-07-22 19:26:55 -0500415 auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable {
Ed Tanous62598e32023-07-17 17:06:25 -0700416 BMCWEB_LOG_DEBUG("Match fired");
Ed Tanousa3e65892021-09-16 14:13:20 -0700417 softwareInterfaceAdded(asyncResp, m, std::move(payload));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500418 };
419
420 fwUpdateInProgress = true;
421
Patrick Williams59d494e2022-07-22 19:26:55 -0500422 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
Andrew Geissler86adcd62019-04-18 10:58:05 -0500423 *crow::connections::systemBus,
424 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
425 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
426 callback);
James Feist4cde5d92020-06-11 10:39:55 -0700427
Patrick Williams59d494e2022-07-22 19:26:55 -0500428 fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>(
James Feist4cde5d92020-06-11 10:39:55 -0700429 *crow::connections::systemBus,
Brian Mae1cc4822021-12-01 17:05:54 +0800430 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
431 "member='InterfacesAdded',"
432 "path='/xyz/openbmc_project/logging'",
Myung Bae8549b952023-08-16 15:18:19 -0400433 std::bind_front(afterUpdateErrorMatcher, asyncResp, url));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500434}
Jennifer Lee729dae72018-04-24 15:59:34 -0700435
Andrew Geissler0554c982019-04-23 14:40:12 -0500436/**
437 * UpdateServiceActionsSimpleUpdate class supports handle POST method for
438 * SimpleUpdate action.
439 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700440inline void requestRoutesUpdateServiceActionsSimpleUpdate(App& app)
Andrew Geissler0554c982019-04-23 14:40:12 -0500441{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700442 BMCWEB_ROUTE(
443 app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
Ed Tanoused398212021-06-09 17:05:54 -0700444 .privileges(redfish::privileges::postUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700445 .methods(boost::beast::http::verb::post)(
446 [&app](const crow::Request& req,
447 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000448 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700449 {
450 return;
451 }
Ed Tanous45ca1b82022-03-25 13:07:27 -0700452
Ed Tanous002d39b2022-05-31 08:59:27 -0700453 std::optional<std::string> transferProtocol;
454 std::string imageURI;
Andrew Geissler0554c982019-04-23 14:40:12 -0500455
Ed Tanous62598e32023-07-17 17:06:25 -0700456 BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost");
Andrew Geissler0554c982019-04-23 14:40:12 -0500457
Ed Tanous002d39b2022-05-31 08:59:27 -0700458 // User can pass in both TransferProtocol and ImageURI parameters or
459 // they can pass in just the ImageURI with the transfer protocol
460 // embedded within it.
461 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
462 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
Andrew Geissler0554c982019-04-23 14:40:12 -0500463
Ed Tanous002d39b2022-05-31 08:59:27 -0700464 if (!json_util::readJsonAction(req, asyncResp->res, "TransferProtocol",
465 transferProtocol, "ImageURI", imageURI))
466 {
Ed Tanous62598e32023-07-17 17:06:25 -0700467 BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter");
Ed Tanous002d39b2022-05-31 08:59:27 -0700468 return;
469 }
470 if (!transferProtocol)
471 {
472 // Must be option 2
473 // Verify ImageURI has transfer protocol in it
474 size_t separator = imageURI.find(':');
Andrew Geissler0554c982019-04-23 14:40:12 -0500475 if ((separator == std::string::npos) ||
476 ((separator + 1) > imageURI.size()))
477 {
478 messages::actionParameterValueTypeError(
479 asyncResp->res, imageURI, "ImageURI",
480 "UpdateService.SimpleUpdate");
Ed Tanous62598e32023-07-17 17:06:25 -0700481 BMCWEB_LOG_ERROR("ImageURI missing transfer protocol: {}",
482 imageURI);
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530483 return;
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530484 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700485 transferProtocol = imageURI.substr(0, separator);
486 // Ensure protocol is upper case for a common comparison path
487 // below
488 boost::to_upper(*transferProtocol);
Ed Tanous62598e32023-07-17 17:06:25 -0700489 BMCWEB_LOG_DEBUG("Encoded transfer protocol {}", *transferProtocol);
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530490
Ed Tanous002d39b2022-05-31 08:59:27 -0700491 // Adjust imageURI to not have the protocol on it for parsing
492 // below
493 // ex. tftp://1.1.1.1/myfile.bin -> 1.1.1.1/myfile.bin
494 imageURI = imageURI.substr(separator + 3);
Ed Tanous62598e32023-07-17 17:06:25 -0700495 BMCWEB_LOG_DEBUG("Adjusted imageUri {}", imageURI);
Ed Tanous002d39b2022-05-31 08:59:27 -0700496 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530497
Ed Tanous002d39b2022-05-31 08:59:27 -0700498 // OpenBMC currently only supports TFTP
499 if (*transferProtocol != "TFTP")
500 {
501 messages::actionParameterNotSupported(asyncResp->res,
502 "TransferProtocol",
503 "UpdateService.SimpleUpdate");
Ed Tanous62598e32023-07-17 17:06:25 -0700504 BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}",
505 *transferProtocol);
Ed Tanous002d39b2022-05-31 08:59:27 -0700506 return;
507 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700508
Ed Tanous002d39b2022-05-31 08:59:27 -0700509 // Format should be <IP or Hostname>/<file> for imageURI
510 size_t separator = imageURI.find('/');
511 if ((separator == std::string::npos) ||
512 ((separator + 1) > imageURI.size()))
513 {
514 messages::actionParameterValueTypeError(
515 asyncResp->res, imageURI, "ImageURI",
516 "UpdateService.SimpleUpdate");
Ed Tanous62598e32023-07-17 17:06:25 -0700517 BMCWEB_LOG_ERROR("Invalid ImageURI: {}", imageURI);
Ed Tanous002d39b2022-05-31 08:59:27 -0700518 return;
519 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700520
Ed Tanous002d39b2022-05-31 08:59:27 -0700521 std::string tftpServer = imageURI.substr(0, separator);
522 std::string fwFile = imageURI.substr(separator + 1);
Ed Tanous62598e32023-07-17 17:06:25 -0700523 BMCWEB_LOG_DEBUG("Server: {}{}", tftpServer + " File: ", fwFile);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700524
Ed Tanous002d39b2022-05-31 08:59:27 -0700525 // Setup callback for when new software detected
526 // Give TFTP 10 minutes to complete
527 monitorForSoftwareAvailable(
528 asyncResp, req,
529 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
530 600);
531
532 // TFTP can take up to 10 minutes depending on image size and
533 // connection speed. Return to caller as soon as the TFTP operation
534 // has been started. The callback above will ensure the activate
535 // is started once the download has completed
536 redfish::messages::success(asyncResp->res);
537
538 // Call TFTP service
539 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800540 [](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700541 if (ec)
542 {
543 // messages::internalError(asyncResp->res);
544 cleanUp();
Ed Tanous62598e32023-07-17 17:06:25 -0700545 BMCWEB_LOG_DEBUG("error_code = {}", ec);
546 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
Ed Tanous002d39b2022-05-31 08:59:27 -0700547 }
548 else
549 {
Ed Tanous62598e32023-07-17 17:06:25 -0700550 BMCWEB_LOG_DEBUG("Call to DownloaViaTFTP Success");
Ed Tanous002d39b2022-05-31 08:59:27 -0700551 }
Patrick Williams5a39f772023-10-20 11:20:21 -0500552 },
Ed Tanous002d39b2022-05-31 08:59:27 -0700553 "xyz.openbmc_project.Software.Download",
554 "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP",
555 "DownloadViaTFTP", fwFile, tftpServer);
556
Ed Tanous62598e32023-07-17 17:06:25 -0700557 BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost");
Patrick Williams5a39f772023-10-20 11:20:21 -0500558 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700559}
560
George Liu0ed80c82020-05-12 16:06:27 +0800561inline void uploadImageFile(crow::Response& res, std::string_view body)
562{
Ed Tanous2c6ffdb2023-06-28 11:28:38 -0700563 std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID());
564
Ed Tanous62598e32023-07-17 17:06:25 -0700565 BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string());
George Liu0ed80c82020-05-12 16:06:27 +0800566 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
567 std::ofstream::trunc);
568 // set the permission of the file to 640
Patrick Williams89492a12023-05-10 07:51:34 -0500569 std::filesystem::perms permission = std::filesystem::perms::owner_read |
570 std::filesystem::perms::group_read;
George Liu0ed80c82020-05-12 16:06:27 +0800571 std::filesystem::permissions(filepath, permission);
572 out << body;
573
574 if (out.bad())
575 {
576 messages::internalError(res);
577 cleanUp();
578 }
579}
580
581inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
582 const std::string& applyTime)
583{
584 std::string applyTimeNewVal;
585 if (applyTime == "Immediate")
586 {
587 applyTimeNewVal =
588 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
589 }
590 else if (applyTime == "OnReset")
591 {
592 applyTimeNewVal =
593 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
594 }
595 else
596 {
Ed Tanous62598e32023-07-17 17:06:25 -0700597 BMCWEB_LOG_INFO(
598 "ApplyTime value is not in the list of acceptable values");
George Liu0ed80c82020-05-12 16:06:27 +0800599 messages::propertyValueNotInList(asyncResp->res, applyTime,
600 "ApplyTime");
601 return;
602 }
603
604 // Set the requested image apply time value
George Liu9ae226f2023-06-21 17:56:46 +0800605 sdbusplus::asio::setProperty(
606 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
George Liu0ed80c82020-05-12 16:06:27 +0800607 "/xyz/openbmc_project/software/apply_time",
George Liu0ed80c82020-05-12 16:06:27 +0800608 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
George Liu9ae226f2023-06-21 17:56:46 +0800609 applyTimeNewVal, [asyncResp](const boost::system::error_code& ec) {
Patrick Williams5a39f772023-10-20 11:20:21 -0500610 if (ec)
611 {
612 BMCWEB_LOG_ERROR("D-Bus responses error: {}", ec);
613 messages::internalError(asyncResp->res);
614 return;
615 }
616 messages::success(asyncResp->res);
617 });
George Liu0ed80c82020-05-12 16:06:27 +0800618}
619
620inline void
621 updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
622 const MultipartParser& parser)
623{
624 const std::string* uploadData = nullptr;
625 std::optional<std::string> applyTime = "OnReset";
626 bool targetFound = false;
627 for (const FormPart& formpart : parser.mime_fields)
628 {
629 boost::beast::http::fields::const_iterator it =
630 formpart.fields.find("Content-Disposition");
631 if (it == formpart.fields.end())
632 {
Ed Tanous62598e32023-07-17 17:06:25 -0700633 BMCWEB_LOG_ERROR("Couldn't find Content-Disposition");
George Liu0ed80c82020-05-12 16:06:27 +0800634 return;
635 }
Ed Tanous62598e32023-07-17 17:06:25 -0700636 BMCWEB_LOG_INFO("Parsing value {}", it->value());
George Liu0ed80c82020-05-12 16:06:27 +0800637
638 // The construction parameters of param_list must start with `;`
639 size_t index = it->value().find(';');
640 if (index == std::string::npos)
641 {
642 continue;
643 }
644
Patrick Williams89492a12023-05-10 07:51:34 -0500645 for (const auto& param :
George Liu0ed80c82020-05-12 16:06:27 +0800646 boost::beast::http::param_list{it->value().substr(index)})
647 {
648 if (param.first != "name" || param.second.empty())
649 {
650 continue;
651 }
652
653 if (param.second == "UpdateParameters")
654 {
655 std::vector<std::string> targets;
656 nlohmann::json content =
657 nlohmann::json::parse(formpart.content);
658 if (!json_util::readJson(content, asyncResp->res, "Targets",
659 targets, "@Redfish.OperationApplyTime",
660 applyTime))
661 {
662 return;
663 }
664 if (targets.size() != 1)
665 {
666 messages::propertyValueFormatError(asyncResp->res,
667 "Targets", "");
668 return;
669 }
670 if (targets[0] != "/redfish/v1/Managers/bmc")
671 {
672 messages::propertyValueNotInList(asyncResp->res,
673 "Targets/0", targets[0]);
674 return;
675 }
676 targetFound = true;
677 }
678 else if (param.second == "UpdateFile")
679 {
680 uploadData = &(formpart.content);
681 }
682 }
683 }
684
685 if (uploadData == nullptr)
686 {
Ed Tanous62598e32023-07-17 17:06:25 -0700687 BMCWEB_LOG_ERROR("Upload data is NULL");
George Liu0ed80c82020-05-12 16:06:27 +0800688 messages::propertyMissing(asyncResp->res, "UpdateFile");
689 return;
690 }
691 if (!targetFound)
692 {
693 messages::propertyMissing(asyncResp->res, "targets");
694 return;
695 }
696
697 setApplyTime(asyncResp, *applyTime);
698
699 uploadImageFile(asyncResp->res, *uploadData);
700}
701
Ed Tanousc2051d12022-05-11 12:21:55 -0700702inline void
703 handleUpdateServicePost(App& app, const crow::Request& req,
704 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
705{
Carson Labrado3ba00072022-06-06 19:40:56 +0000706 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanousc2051d12022-05-11 12:21:55 -0700707 {
708 return;
709 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500710 std::string_view contentType = req.getHeaderValue("Content-Type");
Ed Tanousc2051d12022-05-11 12:21:55 -0700711
Ed Tanous62598e32023-07-17 17:06:25 -0700712 BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType);
Ed Tanousc2051d12022-05-11 12:21:55 -0700713
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500714 // Make sure that content type is application/octet-stream or
715 // multipart/form-data
716 if (boost::iequals(contentType, "application/octet-stream"))
George Liu0ed80c82020-05-12 16:06:27 +0800717 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500718 // Setup callback for when new software detected
719 monitorForSoftwareAvailable(asyncResp, req,
720 "/redfish/v1/UpdateService");
721
George Liu0ed80c82020-05-12 16:06:27 +0800722 uploadImageFile(asyncResp->res, req.body());
George Liu0ed80c82020-05-12 16:06:27 +0800723 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500724 else if (contentType.starts_with("multipart/form-data"))
George Liu0ed80c82020-05-12 16:06:27 +0800725 {
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500726 MultipartParser parser;
727
728 // Setup callback for when new software detected
729 monitorForSoftwareAvailable(asyncResp, req,
730 "/redfish/v1/UpdateService");
731
732 ParserError ec = parser.parse(req);
733 if (ec != ParserError::PARSER_SUCCESS)
734 {
735 // handle error
Ed Tanous62598e32023-07-17 17:06:25 -0700736 BMCWEB_LOG_ERROR("MIME parse failed, ec : {}",
737 static_cast<int>(ec));
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500738 messages::internalError(asyncResp->res);
739 return;
740 }
741 updateMultipartContext(asyncResp, parser);
George Liu0ed80c82020-05-12 16:06:27 +0800742 }
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500743 else
744 {
Ed Tanous62598e32023-07-17 17:06:25 -0700745 BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType);
Ninad Palsuleb33a4322023-06-09 09:19:18 -0500746 asyncResp->res.result(boost::beast::http::status::bad_request);
747 }
Ed Tanousc2051d12022-05-11 12:21:55 -0700748}
749
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700750inline void requestRoutesUpdateService(App& app)
751{
752 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700753 .privileges(redfish::privileges::getUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700754 .methods(boost::beast::http::verb::get)(
755 [&app](const crow::Request& req,
756 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000757 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700758 {
759 return;
760 }
761 asyncResp->res.jsonValue["@odata.type"] =
George Liu0ed80c82020-05-12 16:06:27 +0800762 "#UpdateService.v1_11_1.UpdateService";
Ed Tanous002d39b2022-05-31 08:59:27 -0700763 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
764 asyncResp->res.jsonValue["Id"] = "UpdateService";
765 asyncResp->res.jsonValue["Description"] = "Service for Software Update";
766 asyncResp->res.jsonValue["Name"] = "Update Service";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700767
Ed Tanous002d39b2022-05-31 08:59:27 -0700768 asyncResp->res.jsonValue["HttpPushUri"] =
769 "/redfish/v1/UpdateService/update";
George Liu0ed80c82020-05-12 16:06:27 +0800770 asyncResp->res.jsonValue["MultipartHttpPushUri"] =
771 "/redfish/v1/UpdateService/update";
Ed Tanous4dc23f32022-05-11 11:32:19 -0700772
Ed Tanous002d39b2022-05-31 08:59:27 -0700773 // UpdateService cannot be disabled
774 asyncResp->res.jsonValue["ServiceEnabled"] = true;
775 asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
776 "/redfish/v1/UpdateService/FirmwareInventory";
777 // Get the MaxImageSizeBytes
778 asyncResp->res.jsonValue["MaxImageSizeBytes"] =
779 bmcwebHttpReqBodyLimitMb * 1024 * 1024;
Tejas Patild61e5192021-06-04 15:49:35 +0530780
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700781#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
Ed Tanous002d39b2022-05-31 08:59:27 -0700782 // Update Actions object.
783 nlohmann::json& updateSvcSimpleUpdate =
784 asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
785 updateSvcSimpleUpdate["target"] =
786 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
787 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = {
788 "TFTP"};
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700789#endif
Ed Tanous002d39b2022-05-31 08:59:27 -0700790 // Get the current ApplyTime value
791 sdbusplus::asio::getProperty<std::string>(
792 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
793 "/xyz/openbmc_project/software/apply_time",
794 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800795 [asyncResp](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -0700796 const std::string& applyTime) {
797 if (ec)
798 {
Ed Tanous62598e32023-07-17 17:06:25 -0700799 BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
Ed Tanous002d39b2022-05-31 08:59:27 -0700800 messages::internalError(asyncResp->res);
801 return;
802 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530803
Ed Tanous002d39b2022-05-31 08:59:27 -0700804 // Store the ApplyTime Value
805 if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
806 "RequestedApplyTimes.Immediate")
807 {
808 asyncResp->res.jsonValue["HttpPushUriOptions"]
809 ["HttpPushUriApplyTime"]["ApplyTime"] =
810 "Immediate";
811 }
812 else if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
813 "RequestedApplyTimes.OnReset")
814 {
815 asyncResp->res.jsonValue["HttpPushUriOptions"]
816 ["HttpPushUriApplyTime"]["ApplyTime"] =
817 "OnReset";
818 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700819 });
Patrick Williams5a39f772023-10-20 11:20:21 -0500820 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700821 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700822 .privileges(redfish::privileges::patchUpdateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700823 .methods(boost::beast::http::verb::patch)(
824 [&app](const crow::Request& req,
825 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000826 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700827 {
828 return;
829 }
Ed Tanous62598e32023-07-17 17:06:25 -0700830 BMCWEB_LOG_DEBUG("doPatch...");
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530831
Ed Tanous002d39b2022-05-31 08:59:27 -0700832 std::optional<nlohmann::json> pushUriOptions;
833 if (!json_util::readJsonPatch(req, asyncResp->res, "HttpPushUriOptions",
834 pushUriOptions))
835 {
836 return;
837 }
838
839 if (pushUriOptions)
840 {
841 std::optional<nlohmann::json> pushUriApplyTime;
842 if (!json_util::readJson(*pushUriOptions, asyncResp->res,
843 "HttpPushUriApplyTime", pushUriApplyTime))
George Liu0fda0f12021-11-16 10:06:17 +0800844 {
845 return;
846 }
847
Ed Tanous002d39b2022-05-31 08:59:27 -0700848 if (pushUriApplyTime)
George Liu0fda0f12021-11-16 10:06:17 +0800849 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700850 std::optional<std::string> applyTime;
851 if (!json_util::readJson(*pushUriApplyTime, asyncResp->res,
852 "ApplyTime", applyTime))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700853 {
Ed Tanousc711bf82018-07-30 16:31:33 -0700854 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700855 }
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700856
Ed Tanous002d39b2022-05-31 08:59:27 -0700857 if (applyTime)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700858 {
George Liu0ed80c82020-05-12 16:06:27 +0800859 setApplyTime(asyncResp, *applyTime);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700860 }
George Liu0fda0f12021-11-16 10:06:17 +0800861 }
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