blob: cd49f577e108d19ffc57b205814e9436d5e6eef5 [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
John Edward Broadbent7e860f12021-04-08 15:57:16 -070020#include <app.hpp>
Jennifer Lee729dae72018-04-24 15:59:34 -070021#include <boost/container/flat_map.hpp>
Ed Tanous168e20c2021-12-13 14:39:53 -080022#include <dbus_utility.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070023#include <registries/privilege_registry.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070024#include <sdbusplus/asio/property.hpp>
Andrew Geissler87d84722019-02-28 14:28:39 -060025#include <utils/fw_utils.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050026
Ed Tanous1abe55e2018-09-05 08:30:59 -070027namespace redfish
28{
Ed Tanous27826b52018-10-29 11:40:58 -070029
Andrew Geissler0e7de462019-03-04 19:11:54 -060030// Match signals added on software path
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070031static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher;
James Feist4cde5d92020-06-11 10:39:55 -070032static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateErrorMatcher;
Andrew Geissler0e7de462019-03-04 19:11:54 -060033// Only allow one update at a time
34static bool fwUpdateInProgress = false;
Andrew Geissler86adcd62019-04-18 10:58:05 -050035// Timer for software available
Ed Tanous271584a2019-07-09 16:24:22 -070036static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
Andrew Geissler86adcd62019-04-18 10:58:05 -050037
John Edward Broadbent7e860f12021-04-08 15:57:16 -070038inline static void cleanUp()
Andrew Geissler86adcd62019-04-18 10:58:05 -050039{
40 fwUpdateInProgress = false;
41 fwUpdateMatcher = nullptr;
James Feist4cde5d92020-06-11 10:39:55 -070042 fwUpdateErrorMatcher = nullptr;
Andrew Geissler86adcd62019-04-18 10:58:05 -050043}
John Edward Broadbent7e860f12021-04-08 15:57:16 -070044inline static void activateImage(const std::string& objPath,
45 const std::string& service)
Andrew Geissler86adcd62019-04-18 10:58:05 -050046{
47 BMCWEB_LOG_DEBUG << "Activate image for " << objPath << " " << service;
48 crow::connections::systemBus->async_method_call(
Ed Tanous81ce6092020-12-17 16:54:55 +000049 [](const boost::system::error_code errorCode) {
50 if (errorCode)
Andrew Geissler86adcd62019-04-18 10:58:05 -050051 {
Ed Tanous81ce6092020-12-17 16:54:55 +000052 BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
53 BMCWEB_LOG_DEBUG << "error msg = " << errorCode.message();
Andrew Geissler86adcd62019-04-18 10:58:05 -050054 }
55 },
56 service, objPath, "org.freedesktop.DBus.Properties", "Set",
57 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
Ed Tanous168e20c2021-12-13 14:39:53 -080058 dbus::utility::DbusVariantType(
George Liu0fda0f12021-11-16 10:06:17 +080059 "xyz.openbmc_project.Software.Activation.RequestedActivations.Active"));
Andrew Geissler86adcd62019-04-18 10:58:05 -050060}
Andrew Geissler0554c982019-04-23 14:40:12 -050061
62// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
63// then no asyncResp updates will occur
zhanghch058d1b46d2021-04-01 11:18:24 +080064static void
65 softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
66 sdbusplus::message::message& m,
Ed Tanousa3e65892021-09-16 14:13:20 -070067 task::Payload&& payload)
Andrew Geissler86adcd62019-04-18 10:58:05 -050068{
69 std::vector<std::pair<
70 std::string,
Ed Tanous168e20c2021-12-13 14:39:53 -080071 std::vector<std::pair<std::string, dbus::utility::DbusVariantType>>>>
Andrew Geissler86adcd62019-04-18 10:58:05 -050072 interfacesProperties;
73
74 sdbusplus::message::object_path objPath;
75
76 m.read(objPath, interfacesProperties);
77
78 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
Gunnar Mills1214b7e2020-06-04 10:11:30 -050079 for (auto& interface : interfacesProperties)
Andrew Geissler86adcd62019-04-18 10:58:05 -050080 {
81 BMCWEB_LOG_DEBUG << "interface = " << interface.first;
82
83 if (interface.first == "xyz.openbmc_project.Software.Activation")
84 {
Andrew Geissler86adcd62019-04-18 10:58:05 -050085 // Retrieve service and activate
86 crow::connections::systemBus->async_method_call(
Ed Tanousa3e65892021-09-16 14:13:20 -070087 [objPath, asyncResp, payload(std::move(payload))](
88 const boost::system::error_code errorCode,
89 const std::vector<
90 std::pair<std::string, std::vector<std::string>>>&
91 objInfo) mutable {
Ed Tanous81ce6092020-12-17 16:54:55 +000092 if (errorCode)
Andrew Geissler86adcd62019-04-18 10:58:05 -050093 {
Ed Tanous81ce6092020-12-17 16:54:55 +000094 BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
Andrew Geissler86adcd62019-04-18 10:58:05 -050095 BMCWEB_LOG_DEBUG << "error msg = "
Ed Tanous81ce6092020-12-17 16:54:55 +000096 << errorCode.message();
Andrew Geissler0554c982019-04-23 14:40:12 -050097 if (asyncResp)
98 {
99 messages::internalError(asyncResp->res);
100 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500101 cleanUp();
102 return;
103 }
104 // Ensure we only got one service back
105 if (objInfo.size() != 1)
106 {
107 BMCWEB_LOG_ERROR << "Invalid Object Size "
108 << objInfo.size();
Andrew Geissler0554c982019-04-23 14:40:12 -0500109 if (asyncResp)
110 {
111 messages::internalError(asyncResp->res);
112 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500113 cleanUp();
114 return;
115 }
116 // cancel timer only when
117 // xyz.openbmc_project.Software.Activation interface
118 // is added
119 fwAvailableTimer = nullptr;
120
121 activateImage(objPath.str, objInfo[0].first);
Andrew Geissler0554c982019-04-23 14:40:12 -0500122 if (asyncResp)
123 {
James Feist32898ce2020-03-10 16:16:52 -0700124 std::shared_ptr<task::TaskData> task =
125 task::TaskData::createTask(
126 [](boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500127 sdbusplus::message::message& msg,
128 const std::shared_ptr<task::TaskData>&
129 taskData) {
James Feist32898ce2020-03-10 16:16:52 -0700130 if (ec)
131 {
132 return task::completed;
133 }
134
135 std::string iface;
136 boost::container::flat_map<
James Feistfd9ab9e2020-05-19 13:48:07 -0700137 std::string,
Ed Tanous168e20c2021-12-13 14:39:53 -0800138 dbus::utility::DbusVariantType>
James Feist32898ce2020-03-10 16:16:52 -0700139 values;
James Feist32898ce2020-03-10 16:16:52 -0700140
James Feiste5d50062020-05-11 17:29:00 -0700141 std::string index =
142 std::to_string(taskData->index);
James Feistfd9ab9e2020-05-19 13:48:07 -0700143 msg.read(iface, values);
James Feiste5d50062020-05-11 17:29:00 -0700144
George Liu0fda0f12021-11-16 10:06:17 +0800145 if (iface ==
146 "xyz.openbmc_project.Software.Activation")
James Feist32898ce2020-03-10 16:16:52 -0700147 {
James Feistfd9ab9e2020-05-19 13:48:07 -0700148 auto findActivation =
149 values.find("Activation");
150 if (findActivation == values.end())
151 {
152 return !task::completed;
153 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500154 std::string* state =
James Feistfd9ab9e2020-05-19 13:48:07 -0700155 std::get_if<std::string>(
156 &(findActivation->second));
157
158 if (state == nullptr)
159 {
160 taskData->messages.emplace_back(
161 messages::internalError());
162 return task::completed;
163 }
164
165 if (boost::ends_with(*state,
166 "Invalid") ||
167 boost::ends_with(*state, "Failed"))
168 {
169 taskData->state = "Exception";
170 taskData->status = "Warning";
171 taskData->messages.emplace_back(
172 messages::taskAborted(index));
173 return task::completed;
174 }
175
176 if (boost::ends_with(*state, "Staged"))
177 {
178 taskData->state = "Stopping";
179 taskData->messages.emplace_back(
180 messages::taskPaused(index));
181
182 // its staged, set a long timer to
183 // allow them time to complete the
184 // update (probably cycle the
185 // system) if this expires then
186 // task will be cancelled
187 taskData->extendTimer(
188 std::chrono::hours(5));
189 return !task::completed;
190 }
191
192 if (boost::ends_with(*state, "Active"))
193 {
194 taskData->messages.emplace_back(
195 messages::taskCompletedOK(
196 index));
197 taskData->state = "Completed";
198 return task::completed;
199 }
James Feist32898ce2020-03-10 16:16:52 -0700200 }
George Liu0fda0f12021-11-16 10:06:17 +0800201 else if (
202 iface ==
203 "xyz.openbmc_project.Software.ActivationProgress")
James Feist32898ce2020-03-10 16:16:52 -0700204 {
James Feistfd9ab9e2020-05-19 13:48:07 -0700205 auto findProgress =
206 values.find("Progress");
207 if (findProgress == values.end())
208 {
209 return !task::completed;
210 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500211 uint8_t* progress =
James Feistfd9ab9e2020-05-19 13:48:07 -0700212 std::get_if<uint8_t>(
213 &(findProgress->second));
James Feist32898ce2020-03-10 16:16:52 -0700214
James Feistfd9ab9e2020-05-19 13:48:07 -0700215 if (progress == nullptr)
216 {
217 taskData->messages.emplace_back(
218 messages::internalError());
219 return task::completed;
220 }
George Liu6868ff52021-01-02 11:37:41 +0800221 taskData->percentComplete =
222 static_cast<int>(*progress);
James Feist32898ce2020-03-10 16:16:52 -0700223 taskData->messages.emplace_back(
James Feistfd9ab9e2020-05-19 13:48:07 -0700224 messages::taskProgressChanged(
225 index, static_cast<size_t>(
226 *progress)));
227
228 // if we're getting status updates it's
229 // still alive, update timer
230 taskData->extendTimer(
231 std::chrono::minutes(5));
James Feist32898ce2020-03-10 16:16:52 -0700232 }
233
234 // as firmware update often results in a
235 // reboot, the task may never "complete"
236 // unless it is an error
237
238 return !task::completed;
239 },
George Liu0fda0f12021-11-16 10:06:17 +0800240 "type='signal',interface='org.freedesktop.DBus.Properties',"
James Feistfd9ab9e2020-05-19 13:48:07 -0700241 "member='PropertiesChanged',path='" +
James Feist32898ce2020-03-10 16:16:52 -0700242 objPath.str + "'");
243 task->startTimer(std::chrono::minutes(5));
244 task->populateResp(asyncResp->res);
Ed Tanousa3e65892021-09-16 14:13:20 -0700245 task->payload.emplace(std::move(payload));
Andrew Geissler0554c982019-04-23 14:40:12 -0500246 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500247 fwUpdateInProgress = false;
248 },
249 "xyz.openbmc_project.ObjectMapper",
250 "/xyz/openbmc_project/object_mapper",
251 "xyz.openbmc_project.ObjectMapper", "GetObject", objPath.str,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500252 std::array<const char*, 1>{
Andrew Geissler86adcd62019-04-18 10:58:05 -0500253 "xyz.openbmc_project.Software.Activation"});
254 }
255 }
256}
257
Andrew Geissler0554c982019-04-23 14:40:12 -0500258// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
259// then no asyncResp updates will occur
Ed Tanousb5a76932020-09-29 16:16:58 -0700260static void monitorForSoftwareAvailable(
zhanghch058d1b46d2021-04-01 11:18:24 +0800261 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
262 const crow::Request& req, const std::string& url,
263 int timeoutTimeSeconds = 10)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500264{
265 // Only allow one FW update at a time
Ed Tanouse05aec52022-01-25 10:28:56 -0800266 if (fwUpdateInProgress)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500267 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500268 if (asyncResp)
269 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500270 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
271 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500272 return;
273 }
274
Andrew Geissler0554c982019-04-23 14:40:12 -0500275 fwAvailableTimer =
Ed Tanous271584a2019-07-09 16:24:22 -0700276 std::make_unique<boost::asio::steady_timer>(*req.ioService);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500277
Ed Tanous271584a2019-07-09 16:24:22 -0700278 fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500279
280 fwAvailableTimer->async_wait(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500281 [asyncResp](const boost::system::error_code& ec) {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500282 cleanUp();
283 if (ec == boost::asio::error::operation_aborted)
284 {
285 // expected, we were canceled before the timer completed.
286 return;
287 }
288 BMCWEB_LOG_ERROR
289 << "Timed out waiting for firmware object being created";
290 BMCWEB_LOG_ERROR
291 << "FW image may has already been uploaded to server";
292 if (ec)
293 {
294 BMCWEB_LOG_ERROR << "Async_wait failed" << ec;
295 return;
296 }
Andrew Geissler0554c982019-04-23 14:40:12 -0500297 if (asyncResp)
298 {
299 redfish::messages::internalError(asyncResp->res);
300 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500301 });
Ed Tanousa3e65892021-09-16 14:13:20 -0700302 task::Payload payload(req);
303 auto callback = [asyncResp,
304 payload](sdbusplus::message::message& m) mutable {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500305 BMCWEB_LOG_DEBUG << "Match fired";
Ed Tanousa3e65892021-09-16 14:13:20 -0700306 softwareInterfaceAdded(asyncResp, m, std::move(payload));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500307 };
308
309 fwUpdateInProgress = true;
310
311 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
312 *crow::connections::systemBus,
313 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
314 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
315 callback);
James Feist4cde5d92020-06-11 10:39:55 -0700316
317 fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match::match>(
318 *crow::connections::systemBus,
Brian Mae1cc4822021-12-01 17:05:54 +0800319 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
320 "member='InterfacesAdded',"
321 "path='/xyz/openbmc_project/logging'",
James Feist4cde5d92020-06-11 10:39:55 -0700322 [asyncResp, url](sdbusplus::message::message& m) {
Brian Mae1cc4822021-12-01 17:05:54 +0800323 std::vector<
324 std::pair<std::string, dbus::utility::DBusPropertiesMap>>
325 interfacesProperties;
326 sdbusplus::message::object_path objPath;
327 m.read(objPath, interfacesProperties);
328 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
329 for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
330 interface : interfacesProperties)
James Feist4cde5d92020-06-11 10:39:55 -0700331 {
Brian Mae1cc4822021-12-01 17:05:54 +0800332 if (interface.first == "xyz.openbmc_project.Logging.Entry")
333 {
Ed Tanous711ac7a2021-12-20 09:34:41 -0800334 for (const std::pair<std::string,
335 dbus::utility::DbusVariantType>&
336 value : interface.second)
Brian Mae1cc4822021-12-01 17:05:54 +0800337 {
Ed Tanous711ac7a2021-12-20 09:34:41 -0800338 if (value.first != "Message")
339 {
340 continue;
341 }
342 const std::string* type =
343 std::get_if<std::string>(&value.second);
344 if (type == nullptr)
345 {
346 // if this was our message, timeout will cover it
347 return;
348 }
349 fwAvailableTimer = nullptr;
350 if (*type ==
351 "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
352 {
353 redfish::messages::invalidUpload(
354 asyncResp->res, url, "Invalid archive");
355 }
356 else if (*type ==
357 "xyz.openbmc_project.Software.Image.Error."
358 "ManifestFileFailure")
359 {
360 redfish::messages::invalidUpload(
361 asyncResp->res, url, "Invalid manifest");
362 }
363 else if (
364 *type ==
365 "xyz.openbmc_project.Software.Image.Error.ImageFailure")
366 {
367 redfish::messages::invalidUpload(
368 asyncResp->res, url, "Invalid image format");
369 }
370 else if (
371 *type ==
372 "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
373 {
374 redfish::messages::invalidUpload(
375 asyncResp->res, url,
376 "Image version already exists");
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600377
Ed Tanous711ac7a2021-12-20 09:34:41 -0800378 redfish::messages::resourceAlreadyExists(
379 asyncResp->res,
380 "UpdateService.v1_5_0.UpdateService", "Version",
381 "uploaded version");
382 }
383 else if (
384 *type ==
385 "xyz.openbmc_project.Software.Image.Error.BusyFailure")
386 {
387 redfish::messages::resourceExhaustion(
388 asyncResp->res, url);
389 }
390 else
391 {
392 redfish::messages::internalError(asyncResp->res);
393 }
Brian Mae1cc4822021-12-01 17:05:54 +0800394 }
395 }
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600396 }
James Feist4cde5d92020-06-11 10:39:55 -0700397 });
Andrew Geissler86adcd62019-04-18 10:58:05 -0500398}
Jennifer Lee729dae72018-04-24 15:59:34 -0700399
Andrew Geissler0554c982019-04-23 14:40:12 -0500400/**
401 * UpdateServiceActionsSimpleUpdate class supports handle POST method for
402 * SimpleUpdate action.
403 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700404inline void requestRoutesUpdateServiceActionsSimpleUpdate(App& app)
Andrew Geissler0554c982019-04-23 14:40:12 -0500405{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700406 BMCWEB_ROUTE(
407 app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
Ed Tanoused398212021-06-09 17:05:54 -0700408 .privileges(redfish::privileges::postUpdateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700409 .methods(
410 boost::beast::http::verb::
411 post)([](const crow::Request& req,
412 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
413 std::optional<std::string> transferProtocol;
414 std::string imageURI;
Andrew Geissler0554c982019-04-23 14:40:12 -0500415
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700416 BMCWEB_LOG_DEBUG << "Enter UpdateService.SimpleUpdate doPost";
Andrew Geissler0554c982019-04-23 14:40:12 -0500417
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700418 // User can pass in both TransferProtocol and ImageURI parameters or
419 // they can pass in just the ImageURI with the transfer protocol
420 // embedded within it.
421 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
422 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
Andrew Geissler0554c982019-04-23 14:40:12 -0500423
Willy Tu15ed6782021-12-14 11:03:16 -0800424 if (!json_util::readJsonAction(req, asyncResp->res,
425 "TransferProtocol", transferProtocol,
426 "ImageURI", imageURI))
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700427 {
428 BMCWEB_LOG_DEBUG
429 << "Missing TransferProtocol or ImageURI parameter";
430 return;
431 }
432 if (!transferProtocol)
433 {
434 // Must be option 2
435 // Verify ImageURI has transfer protocol in it
436 size_t separator = imageURI.find(':');
437 if ((separator == std::string::npos) ||
438 ((separator + 1) > imageURI.size()))
439 {
440 messages::actionParameterValueTypeError(
441 asyncResp->res, imageURI, "ImageURI",
442 "UpdateService.SimpleUpdate");
443 BMCWEB_LOG_ERROR << "ImageURI missing transfer protocol: "
444 << imageURI;
445 return;
446 }
447 transferProtocol = imageURI.substr(0, separator);
448 // Ensure protocol is upper case for a common comparison path
449 // below
450 boost::to_upper(*transferProtocol);
451 BMCWEB_LOG_DEBUG << "Encoded transfer protocol "
452 << *transferProtocol;
Andrew Geissler0554c982019-04-23 14:40:12 -0500453
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700454 // Adjust imageURI to not have the protocol on it for parsing
455 // below
456 // ex. tftp://1.1.1.1/myfile.bin -> 1.1.1.1/myfile.bin
457 imageURI = imageURI.substr(separator + 3);
458 BMCWEB_LOG_DEBUG << "Adjusted imageUri " << imageURI;
459 }
460
461 // OpenBMC currently only supports TFTP
462 if (*transferProtocol != "TFTP")
463 {
464 messages::actionParameterNotSupported(
465 asyncResp->res, "TransferProtocol",
466 "UpdateService.SimpleUpdate");
467 BMCWEB_LOG_ERROR << "Request incorrect protocol parameter: "
468 << *transferProtocol;
469 return;
470 }
471
472 // Format should be <IP or Hostname>/<file> for imageURI
473 size_t separator = imageURI.find('/');
Andrew Geissler0554c982019-04-23 14:40:12 -0500474 if ((separator == std::string::npos) ||
475 ((separator + 1) > imageURI.size()))
476 {
477 messages::actionParameterValueTypeError(
478 asyncResp->res, imageURI, "ImageURI",
479 "UpdateService.SimpleUpdate");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700480 BMCWEB_LOG_ERROR << "Invalid ImageURI: " << imageURI;
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530481 return;
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530482 }
483
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700484 std::string tftpServer = imageURI.substr(0, separator);
485 std::string fwFile = imageURI.substr(separator + 1);
486 BMCWEB_LOG_DEBUG << "Server: " << tftpServer + " File: " << fwFile;
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530487
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700488 // Setup callback for when new software detected
489 // Give TFTP 10 minutes to complete
490 monitorForSoftwareAvailable(
491 asyncResp, req,
492 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
493 600);
494
495 // TFTP can take up to 10 minutes depending on image size and
496 // connection speed. Return to caller as soon as the TFTP operation
497 // has been started. The callback above will ensure the activate
498 // is started once the download has completed
499 redfish::messages::success(asyncResp->res);
500
501 // Call TFTP service
502 crow::connections::systemBus->async_method_call(
503 [](const boost::system::error_code ec) {
504 if (ec)
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530505 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700506 // messages::internalError(asyncResp->res);
507 cleanUp();
508 BMCWEB_LOG_DEBUG << "error_code = " << ec;
509 BMCWEB_LOG_DEBUG << "error msg = " << ec.message();
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530510 }
511 else
512 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700513 BMCWEB_LOG_DEBUG << "Call to DownloaViaTFTP Success";
514 }
515 },
516 "xyz.openbmc_project.Software.Download",
517 "/xyz/openbmc_project/software",
518 "xyz.openbmc_project.Common.TFTP", "DownloadViaTFTP", fwFile,
519 tftpServer);
520
521 BMCWEB_LOG_DEBUG << "Exit UpdateService.SimpleUpdate doPost";
522 });
523}
524
525inline void requestRoutesUpdateService(App& app)
526{
527 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700528 .privileges(redfish::privileges::getUpdateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700529 .methods(
530 boost::beast::http::verb::
531 get)([](const crow::Request&,
532 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
533 asyncResp->res.jsonValue["@odata.type"] =
Chicago Duan0588a3b2021-06-10 18:20:36 +0800534 "#UpdateService.v1_5_0.UpdateService";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700535 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
536 asyncResp->res.jsonValue["Id"] = "UpdateService";
537 asyncResp->res.jsonValue["Description"] =
538 "Service for Software Update";
539 asyncResp->res.jsonValue["Name"] = "Update Service";
540 asyncResp->res.jsonValue["HttpPushUri"] =
541 "/redfish/v1/UpdateService";
542 // UpdateService cannot be disabled
543 asyncResp->res.jsonValue["ServiceEnabled"] = true;
544 asyncResp->res.jsonValue["FirmwareInventory"] = {
545 {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}};
Tejas Patild61e5192021-06-04 15:49:35 +0530546 // Get the MaxImageSizeBytes
547 asyncResp->res.jsonValue["MaxImageSizeBytes"] =
548 bmcwebHttpReqBodyLimitMb * 1024 * 1024;
549
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700550#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
551 // Update Actions object.
552 nlohmann::json& updateSvcSimpleUpdate =
553 asyncResp->res
554 .jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
555 updateSvcSimpleUpdate["target"] =
556 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
557 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] =
558 {"TFTP"};
559#endif
560 // Get the current ApplyTime value
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700561 sdbusplus::asio::getProperty<std::string>(
562 *crow::connections::systemBus, "xyz.openbmc_project.Settings",
563 "/xyz/openbmc_project/software/apply_time",
564 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700565 [asyncResp](const boost::system::error_code ec,
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700566 const std::string& applyTime) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700567 if (ec)
568 {
569 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
570 messages::internalError(asyncResp->res);
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530571 return;
572 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530573
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700574 // Store the ApplyTime Value
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700575 if (applyTime == "xyz.openbmc_project.Software.ApplyTime."
576 "RequestedApplyTimes.Immediate")
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700577 {
578 asyncResp->res
579 .jsonValue["HttpPushUriOptions"]
580 ["HttpPushUriApplyTime"]["ApplyTime"] =
581 "Immediate";
582 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700583 else if (applyTime ==
584 "xyz.openbmc_project.Software.ApplyTime."
585 "RequestedApplyTimes.OnReset")
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700586 {
587 asyncResp->res
588 .jsonValue["HttpPushUriOptions"]
589 ["HttpPushUriApplyTime"]["ApplyTime"] =
590 "OnReset";
591 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700592 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700593 });
594 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700595 .privileges(redfish::privileges::patchUpdateService)
George Liu0fda0f12021-11-16 10:06:17 +0800596 .methods(
597 boost::beast::http::verb::
598 patch)([](const crow::Request& req,
599 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
600 BMCWEB_LOG_DEBUG << "doPatch...";
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530601
George Liu0fda0f12021-11-16 10:06:17 +0800602 std::optional<nlohmann::json> pushUriOptions;
Willy Tu15ed6782021-12-14 11:03:16 -0800603 if (!json_util::readJsonPatch(req, asyncResp->res,
604 "HttpPushUriOptions", pushUriOptions))
George Liu0fda0f12021-11-16 10:06:17 +0800605 {
606 return;
607 }
608
609 if (pushUriOptions)
610 {
611 std::optional<nlohmann::json> pushUriApplyTime;
612 if (!json_util::readJson(*pushUriOptions, asyncResp->res,
613 "HttpPushUriApplyTime",
614 pushUriApplyTime))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700615 {
Ed Tanousc711bf82018-07-30 16:31:33 -0700616 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700617 }
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700618
George Liu0fda0f12021-11-16 10:06:17 +0800619 if (pushUriApplyTime)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700620 {
George Liu0fda0f12021-11-16 10:06:17 +0800621 std::optional<std::string> applyTime;
622 if (!json_util::readJson(*pushUriApplyTime, asyncResp->res,
623 "ApplyTime", applyTime))
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700624 {
625 return;
626 }
627
George Liu0fda0f12021-11-16 10:06:17 +0800628 if (applyTime)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700629 {
George Liu0fda0f12021-11-16 10:06:17 +0800630 std::string applyTimeNewVal;
631 if (applyTime == "Immediate")
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700632 {
George Liu0fda0f12021-11-16 10:06:17 +0800633 applyTimeNewVal =
634 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
635 }
636 else if (applyTime == "OnReset")
637 {
638 applyTimeNewVal =
639 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
640 }
641 else
642 {
643 BMCWEB_LOG_INFO
644 << "ApplyTime value is not in the list of acceptable values";
645 messages::propertyValueNotInList(
646 asyncResp->res, *applyTime, "ApplyTime");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700647 return;
648 }
649
George Liu0fda0f12021-11-16 10:06:17 +0800650 // Set the requested image apply time value
651 crow::connections::systemBus->async_method_call(
652 [asyncResp](const boost::system::error_code ec) {
653 if (ec)
654 {
655 BMCWEB_LOG_ERROR
656 << "D-Bus responses error: " << ec;
657 messages::internalError(asyncResp->res);
658 return;
659 }
660 messages::success(asyncResp->res);
661 },
662 "xyz.openbmc_project.Settings",
663 "/xyz/openbmc_project/software/apply_time",
664 "org.freedesktop.DBus.Properties", "Set",
665 "xyz.openbmc_project.Software.ApplyTime",
666 "RequestedApplyTime",
Ed Tanous168e20c2021-12-13 14:39:53 -0800667 dbus::utility::DbusVariantType{applyTimeNewVal});
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700668 }
669 }
George Liu0fda0f12021-11-16 10:06:17 +0800670 }
671 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700672 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700673 .privileges(redfish::privileges::postUpdateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700674 .methods(boost::beast::http::verb::post)(
675 [](const crow::Request& req,
676 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
677 BMCWEB_LOG_DEBUG << "doPost...";
678
679 // Setup callback for when new software detected
680 monitorForSoftwareAvailable(asyncResp, req,
681 "/redfish/v1/UpdateService");
682
683 std::string filepath("/tmp/images/" +
684 boost::uuids::to_string(
685 boost::uuids::random_generator()()));
686 BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
687 std::ofstream out(filepath, std::ofstream::out |
688 std::ofstream::binary |
689 std::ofstream::trunc);
690 out << req.body;
691 out.close();
692 BMCWEB_LOG_DEBUG << "file upload complete!!";
693 });
694}
695
696inline void requestRoutesSoftwareInventoryCollection(App& app)
697{
698 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
Ed Tanoused398212021-06-09 17:05:54 -0700699 .privileges(redfish::privileges::getSoftwareInventoryCollection)
George Liu0fda0f12021-11-16 10:06:17 +0800700 .methods(
701 boost::beast::http::verb::
702 get)([](const crow::Request&,
703 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
704 asyncResp->res.jsonValue["@odata.type"] =
705 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
706 asyncResp->res.jsonValue["@odata.id"] =
707 "/redfish/v1/UpdateService/FirmwareInventory";
708 asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700709
George Liu0fda0f12021-11-16 10:06:17 +0800710 crow::connections::systemBus->async_method_call(
711 [asyncResp](
712 const boost::system::error_code ec,
713 const std::vector<
714 std::pair<std::string,
715 std::vector<std::pair<
716 std::string, std::vector<std::string>>>>>&
717 subtree) {
718 if (ec)
719 {
720 messages::internalError(asyncResp->res);
721 return;
722 }
723 asyncResp->res.jsonValue["Members"] =
724 nlohmann::json::array();
725 asyncResp->res.jsonValue["Members@odata.count"] = 0;
726
Ed Tanous9eb808c2022-01-25 10:19:23 -0800727 for (const auto& obj : subtree)
George Liu0fda0f12021-11-16 10:06:17 +0800728 {
729 sdbusplus::message::object_path path(obj.first);
730 std::string swId = path.filename();
731 if (swId.empty())
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700732 {
733 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +0800734 BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700735 return;
736 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700737
George Liu0fda0f12021-11-16 10:06:17 +0800738 nlohmann::json& members =
739 asyncResp->res.jsonValue["Members"];
740 members.push_back(
741 {{"@odata.id",
742 "/redfish/v1/UpdateService/FirmwareInventory/" +
743 swId}});
744 asyncResp->res.jsonValue["Members@odata.count"] =
745 members.size();
746 }
747 },
748 // Note that only firmware levels associated with a device
749 // are stored under /xyz/openbmc_project/software therefore
750 // to ensure only real FirmwareInventory items are returned,
751 // this full object path must be used here as input to
752 // mapper
753 "xyz.openbmc_project.ObjectMapper",
754 "/xyz/openbmc_project/object_mapper",
755 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
756 "/xyz/openbmc_project/software", static_cast<int32_t>(0),
757 std::array<const char*, 1>{
758 "xyz.openbmc_project.Software.Version"});
759 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700760}
761/* Fill related item links (i.e. bmc, bios) in for inventory */
762inline static void
763 getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
764 const std::string& purpose)
765{
766 if (purpose == fw_util::bmcPurpose)
767 {
768 nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
769 relatedItem.push_back({{"@odata.id", "/redfish/v1/Managers/bmc"}});
770 aResp->res.jsonValue["RelatedItem@odata.count"] = relatedItem.size();
771 }
772 else if (purpose == fw_util::biosPurpose)
773 {
774 nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
775 relatedItem.push_back(
776 {{"@odata.id", "/redfish/v1/Systems/system/Bios"}});
Jiaqing Zhao1a6e51a2022-01-19 19:20:24 +0800777 aResp->res.jsonValue["RelatedItem@odata.count"] = relatedItem.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700778 }
779 else
780 {
781 BMCWEB_LOG_ERROR << "Unknown software purpose " << purpose;
782 }
783}
784
785inline void requestRoutesSoftwareInventory(App& app)
786{
787 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700788 .privileges(redfish::privileges::getSoftwareInventory)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700789 .methods(
790 boost::beast::http::verb::get)([](const crow::Request&,
791 const std::shared_ptr<
792 bmcweb::AsyncResp>& asyncResp,
793 const std::string& param) {
794 std::shared_ptr<std::string> swId =
795 std::make_shared<std::string>(param);
796
797 asyncResp->res.jsonValue["@odata.id"] =
798 "/redfish/v1/UpdateService/FirmwareInventory/" + *swId;
799
800 crow::connections::systemBus->async_method_call(
801 [asyncResp, swId](
802 const boost::system::error_code ec,
803 const std::vector<
804 std::pair<std::string,
805 std::vector<std::pair<
806 std::string, std::vector<std::string>>>>>&
807 subtree) {
808 BMCWEB_LOG_DEBUG << "doGet callback...";
809 if (ec)
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700810 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700811 messages::internalError(asyncResp->res);
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700812 return;
813 }
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700814
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700815 // Ensure we find our input swId, otherwise return an error
816 bool found = false;
817 for (const std::pair<
818 std::string,
819 std::vector<std::pair<
820 std::string, std::vector<std::string>>>>& obj :
821 subtree)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700822 {
Ed Tanouse05aec52022-01-25 10:28:56 -0800823 if (!boost::ends_with(obj.first, *swId))
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700824 {
825 continue;
826 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700827
Ed Tanous26f69762022-01-25 09:49:11 -0800828 if (obj.second.empty())
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700829 {
830 continue;
831 }
832
833 found = true;
834 fw_util::getFwStatus(asyncResp, swId,
835 obj.second[0].first);
836
837 crow::connections::systemBus->async_method_call(
Ed Tanous168e20c2021-12-13 14:39:53 -0800838 [asyncResp,
839 swId](const boost::system::error_code errorCode,
840 const boost::container::flat_map<
841 std::string,
842 dbus::utility::DbusVariantType>&
843 propertiesList) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700844 if (errorCode)
845 {
846 messages::internalError(asyncResp->res);
847 return;
848 }
849 boost::container::flat_map<
Ed Tanous168e20c2021-12-13 14:39:53 -0800850 std::string,
851 dbus::utility::DbusVariantType>::
852 const_iterator it =
853 propertiesList.find("Purpose");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700854 if (it == propertiesList.end())
855 {
856 BMCWEB_LOG_DEBUG
857 << "Can't find property \"Purpose\"!";
858 messages::propertyMissing(asyncResp->res,
859 "Purpose");
860 return;
861 }
862 const std::string* swInvPurpose =
863 std::get_if<std::string>(&it->second);
864 if (swInvPurpose == nullptr)
865 {
George Liu0fda0f12021-11-16 10:06:17 +0800866 BMCWEB_LOG_DEBUG
867 << "wrong types for property\"Purpose\"!";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700868 messages::propertyValueTypeError(
869 asyncResp->res, "", "Purpose");
870 return;
871 }
872
873 BMCWEB_LOG_DEBUG << "swInvPurpose = "
874 << *swInvPurpose;
875 it = propertiesList.find("Version");
876 if (it == propertiesList.end())
877 {
878 BMCWEB_LOG_DEBUG
879 << "Can't find property \"Version\"!";
880 messages::propertyMissing(asyncResp->res,
881 "Version");
882 return;
883 }
884
885 BMCWEB_LOG_DEBUG << "Version found!";
886
887 const std::string* version =
888 std::get_if<std::string>(&it->second);
889
890 if (version == nullptr)
891 {
892 BMCWEB_LOG_DEBUG
893 << "Can't find property \"Version\"!";
894
895 messages::propertyValueTypeError(
896 asyncResp->res, "", "Version");
897 return;
898 }
899 asyncResp->res.jsonValue["Version"] = *version;
900 asyncResp->res.jsonValue["Id"] = *swId;
901
902 // swInvPurpose is of format:
903 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
904 // Translate this to "ABC image"
905 size_t endDesc = swInvPurpose->rfind('.');
906 if (endDesc == std::string::npos)
907 {
908 messages::internalError(asyncResp->res);
909 return;
910 }
911 endDesc++;
912 if (endDesc >= swInvPurpose->size())
913 {
914 messages::internalError(asyncResp->res);
915 return;
916 }
917
918 std::string formatDesc =
919 swInvPurpose->substr(endDesc);
920 asyncResp->res.jsonValue["Description"] =
921 formatDesc + " image";
922 getRelatedItems(asyncResp, *swInvPurpose);
923 },
924 obj.second[0].first, obj.first,
925 "org.freedesktop.DBus.Properties", "GetAll",
926 "xyz.openbmc_project.Software.Version");
927 }
928 if (!found)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700929 {
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800930 BMCWEB_LOG_ERROR << "Input swID " << *swId
931 << " not found!";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700932 messages::resourceMissingAtURI(
933 asyncResp->res,
Ed Tanousace85d62021-10-26 12:45:59 -0700934 crow::utility::urlFromPieces(
935 "redfish", "v1", "UpdateService",
936 "FirmwareInventory", *swId));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700937 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700938 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700939 asyncResp->res.jsonValue["@odata.type"] =
940 "#SoftwareInventory.v1_1_0.SoftwareInventory";
941 asyncResp->res.jsonValue["Name"] = "Software Inventory";
942 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700943
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700944 asyncResp->res.jsonValue["Updateable"] = false;
945 fw_util::getFwUpdateableStatus(asyncResp, swId);
946 },
947 "xyz.openbmc_project.ObjectMapper",
948 "/xyz/openbmc_project/object_mapper",
949 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/",
950 static_cast<int32_t>(0),
951 std::array<const char*, 1>{
952 "xyz.openbmc_project.Software.Version"});
953 });
954}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700955
956} // namespace redfish