blob: f5528e1a7077ea035e3f7b5d91d0fdaf03151f9d [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>
Andrew Geissler87d84722019-02-28 14:28:39 -060024#include <utils/fw_utils.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050025
Ed Tanous1abe55e2018-09-05 08:30:59 -070026namespace redfish
27{
Ed Tanous27826b52018-10-29 11:40:58 -070028
Andrew Geissler0e7de462019-03-04 19:11:54 -060029// Match signals added on software path
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070030static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher;
James Feist4cde5d92020-06-11 10:39:55 -070031static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateErrorMatcher;
Andrew Geissler0e7de462019-03-04 19:11:54 -060032// Only allow one update at a time
33static bool fwUpdateInProgress = false;
Andrew Geissler86adcd62019-04-18 10:58:05 -050034// Timer for software available
Ed Tanous271584a2019-07-09 16:24:22 -070035static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
Andrew Geissler86adcd62019-04-18 10:58:05 -050036
John Edward Broadbent7e860f12021-04-08 15:57:16 -070037inline static void cleanUp()
Andrew Geissler86adcd62019-04-18 10:58:05 -050038{
39 fwUpdateInProgress = false;
40 fwUpdateMatcher = nullptr;
James Feist4cde5d92020-06-11 10:39:55 -070041 fwUpdateErrorMatcher = nullptr;
Andrew Geissler86adcd62019-04-18 10:58:05 -050042}
John Edward Broadbent7e860f12021-04-08 15:57:16 -070043inline static void activateImage(const std::string& objPath,
44 const std::string& service)
Andrew Geissler86adcd62019-04-18 10:58:05 -050045{
46 BMCWEB_LOG_DEBUG << "Activate image for " << objPath << " " << service;
47 crow::connections::systemBus->async_method_call(
Ed Tanous81ce6092020-12-17 16:54:55 +000048 [](const boost::system::error_code errorCode) {
49 if (errorCode)
Andrew Geissler86adcd62019-04-18 10:58:05 -050050 {
Ed Tanous81ce6092020-12-17 16:54:55 +000051 BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
52 BMCWEB_LOG_DEBUG << "error msg = " << errorCode.message();
Andrew Geissler86adcd62019-04-18 10:58:05 -050053 }
54 },
55 service, objPath, "org.freedesktop.DBus.Properties", "Set",
56 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
Ed Tanous168e20c2021-12-13 14:39:53 -080057 dbus::utility::DbusVariantType(
George Liu0fda0f12021-11-16 10:06:17 +080058 "xyz.openbmc_project.Software.Activation.RequestedActivations.Active"));
Andrew Geissler86adcd62019-04-18 10:58:05 -050059}
Andrew Geissler0554c982019-04-23 14:40:12 -050060
61// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
62// then no asyncResp updates will occur
zhanghch058d1b46d2021-04-01 11:18:24 +080063static void
64 softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
65 sdbusplus::message::message& m,
Ed Tanousa3e65892021-09-16 14:13:20 -070066 task::Payload&& payload)
Andrew Geissler86adcd62019-04-18 10:58:05 -050067{
68 std::vector<std::pair<
69 std::string,
Ed Tanous168e20c2021-12-13 14:39:53 -080070 std::vector<std::pair<std::string, dbus::utility::DbusVariantType>>>>
Andrew Geissler86adcd62019-04-18 10:58:05 -050071 interfacesProperties;
72
73 sdbusplus::message::object_path objPath;
74
75 m.read(objPath, interfacesProperties);
76
77 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
Gunnar Mills1214b7e2020-06-04 10:11:30 -050078 for (auto& interface : interfacesProperties)
Andrew Geissler86adcd62019-04-18 10:58:05 -050079 {
80 BMCWEB_LOG_DEBUG << "interface = " << interface.first;
81
82 if (interface.first == "xyz.openbmc_project.Software.Activation")
83 {
Andrew Geissler86adcd62019-04-18 10:58:05 -050084 // Retrieve service and activate
85 crow::connections::systemBus->async_method_call(
Ed Tanousa3e65892021-09-16 14:13:20 -070086 [objPath, asyncResp, payload(std::move(payload))](
87 const boost::system::error_code errorCode,
88 const std::vector<
89 std::pair<std::string, std::vector<std::string>>>&
90 objInfo) mutable {
Ed Tanous81ce6092020-12-17 16:54:55 +000091 if (errorCode)
Andrew Geissler86adcd62019-04-18 10:58:05 -050092 {
Ed Tanous81ce6092020-12-17 16:54:55 +000093 BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
Andrew Geissler86adcd62019-04-18 10:58:05 -050094 BMCWEB_LOG_DEBUG << "error msg = "
Ed Tanous81ce6092020-12-17 16:54:55 +000095 << errorCode.message();
Andrew Geissler0554c982019-04-23 14:40:12 -050096 if (asyncResp)
97 {
98 messages::internalError(asyncResp->res);
99 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500100 cleanUp();
101 return;
102 }
103 // Ensure we only got one service back
104 if (objInfo.size() != 1)
105 {
106 BMCWEB_LOG_ERROR << "Invalid Object Size "
107 << objInfo.size();
Andrew Geissler0554c982019-04-23 14:40:12 -0500108 if (asyncResp)
109 {
110 messages::internalError(asyncResp->res);
111 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500112 cleanUp();
113 return;
114 }
115 // cancel timer only when
116 // xyz.openbmc_project.Software.Activation interface
117 // is added
118 fwAvailableTimer = nullptr;
119
120 activateImage(objPath.str, objInfo[0].first);
Andrew Geissler0554c982019-04-23 14:40:12 -0500121 if (asyncResp)
122 {
James Feist32898ce2020-03-10 16:16:52 -0700123 std::shared_ptr<task::TaskData> task =
124 task::TaskData::createTask(
125 [](boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500126 sdbusplus::message::message& msg,
127 const std::shared_ptr<task::TaskData>&
128 taskData) {
James Feist32898ce2020-03-10 16:16:52 -0700129 if (ec)
130 {
131 return task::completed;
132 }
133
134 std::string iface;
135 boost::container::flat_map<
James Feistfd9ab9e2020-05-19 13:48:07 -0700136 std::string,
Ed Tanous168e20c2021-12-13 14:39:53 -0800137 dbus::utility::DbusVariantType>
James Feist32898ce2020-03-10 16:16:52 -0700138 values;
James Feist32898ce2020-03-10 16:16:52 -0700139
James Feiste5d50062020-05-11 17:29:00 -0700140 std::string index =
141 std::to_string(taskData->index);
James Feistfd9ab9e2020-05-19 13:48:07 -0700142 msg.read(iface, values);
James Feiste5d50062020-05-11 17:29:00 -0700143
George Liu0fda0f12021-11-16 10:06:17 +0800144 if (iface ==
145 "xyz.openbmc_project.Software.Activation")
James Feist32898ce2020-03-10 16:16:52 -0700146 {
James Feistfd9ab9e2020-05-19 13:48:07 -0700147 auto findActivation =
148 values.find("Activation");
149 if (findActivation == values.end())
150 {
151 return !task::completed;
152 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500153 std::string* state =
James Feistfd9ab9e2020-05-19 13:48:07 -0700154 std::get_if<std::string>(
155 &(findActivation->second));
156
157 if (state == nullptr)
158 {
159 taskData->messages.emplace_back(
160 messages::internalError());
161 return task::completed;
162 }
163
164 if (boost::ends_with(*state,
165 "Invalid") ||
166 boost::ends_with(*state, "Failed"))
167 {
168 taskData->state = "Exception";
169 taskData->status = "Warning";
170 taskData->messages.emplace_back(
171 messages::taskAborted(index));
172 return task::completed;
173 }
174
175 if (boost::ends_with(*state, "Staged"))
176 {
177 taskData->state = "Stopping";
178 taskData->messages.emplace_back(
179 messages::taskPaused(index));
180
181 // its staged, set a long timer to
182 // allow them time to complete the
183 // update (probably cycle the
184 // system) if this expires then
185 // task will be cancelled
186 taskData->extendTimer(
187 std::chrono::hours(5));
188 return !task::completed;
189 }
190
191 if (boost::ends_with(*state, "Active"))
192 {
193 taskData->messages.emplace_back(
194 messages::taskCompletedOK(
195 index));
196 taskData->state = "Completed";
197 return task::completed;
198 }
James Feist32898ce2020-03-10 16:16:52 -0700199 }
George Liu0fda0f12021-11-16 10:06:17 +0800200 else if (
201 iface ==
202 "xyz.openbmc_project.Software.ActivationProgress")
James Feist32898ce2020-03-10 16:16:52 -0700203 {
James Feistfd9ab9e2020-05-19 13:48:07 -0700204 auto findProgress =
205 values.find("Progress");
206 if (findProgress == values.end())
207 {
208 return !task::completed;
209 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500210 uint8_t* progress =
James Feistfd9ab9e2020-05-19 13:48:07 -0700211 std::get_if<uint8_t>(
212 &(findProgress->second));
James Feist32898ce2020-03-10 16:16:52 -0700213
James Feistfd9ab9e2020-05-19 13:48:07 -0700214 if (progress == nullptr)
215 {
216 taskData->messages.emplace_back(
217 messages::internalError());
218 return task::completed;
219 }
George Liu6868ff52021-01-02 11:37:41 +0800220 taskData->percentComplete =
221 static_cast<int>(*progress);
James Feist32898ce2020-03-10 16:16:52 -0700222 taskData->messages.emplace_back(
James Feistfd9ab9e2020-05-19 13:48:07 -0700223 messages::taskProgressChanged(
224 index, static_cast<size_t>(
225 *progress)));
226
227 // if we're getting status updates it's
228 // still alive, update timer
229 taskData->extendTimer(
230 std::chrono::minutes(5));
James Feist32898ce2020-03-10 16:16:52 -0700231 }
232
233 // as firmware update often results in a
234 // reboot, the task may never "complete"
235 // unless it is an error
236
237 return !task::completed;
238 },
George Liu0fda0f12021-11-16 10:06:17 +0800239 "type='signal',interface='org.freedesktop.DBus.Properties',"
James Feistfd9ab9e2020-05-19 13:48:07 -0700240 "member='PropertiesChanged',path='" +
James Feist32898ce2020-03-10 16:16:52 -0700241 objPath.str + "'");
242 task->startTimer(std::chrono::minutes(5));
243 task->populateResp(asyncResp->res);
Ed Tanousa3e65892021-09-16 14:13:20 -0700244 task->payload.emplace(std::move(payload));
Andrew Geissler0554c982019-04-23 14:40:12 -0500245 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500246 fwUpdateInProgress = false;
247 },
248 "xyz.openbmc_project.ObjectMapper",
249 "/xyz/openbmc_project/object_mapper",
250 "xyz.openbmc_project.ObjectMapper", "GetObject", objPath.str,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500251 std::array<const char*, 1>{
Andrew Geissler86adcd62019-04-18 10:58:05 -0500252 "xyz.openbmc_project.Software.Activation"});
253 }
254 }
255}
256
Andrew Geissler0554c982019-04-23 14:40:12 -0500257// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
258// then no asyncResp updates will occur
Ed Tanousb5a76932020-09-29 16:16:58 -0700259static void monitorForSoftwareAvailable(
zhanghch058d1b46d2021-04-01 11:18:24 +0800260 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
261 const crow::Request& req, const std::string& url,
262 int timeoutTimeSeconds = 10)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500263{
264 // Only allow one FW update at a time
265 if (fwUpdateInProgress != false)
266 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500267 if (asyncResp)
268 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500269 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
270 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500271 return;
272 }
273
Andrew Geissler0554c982019-04-23 14:40:12 -0500274 fwAvailableTimer =
Ed Tanous271584a2019-07-09 16:24:22 -0700275 std::make_unique<boost::asio::steady_timer>(*req.ioService);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500276
Ed Tanous271584a2019-07-09 16:24:22 -0700277 fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500278
279 fwAvailableTimer->async_wait(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500280 [asyncResp](const boost::system::error_code& ec) {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500281 cleanUp();
282 if (ec == boost::asio::error::operation_aborted)
283 {
284 // expected, we were canceled before the timer completed.
285 return;
286 }
287 BMCWEB_LOG_ERROR
288 << "Timed out waiting for firmware object being created";
289 BMCWEB_LOG_ERROR
290 << "FW image may has already been uploaded to server";
291 if (ec)
292 {
293 BMCWEB_LOG_ERROR << "Async_wait failed" << ec;
294 return;
295 }
Andrew Geissler0554c982019-04-23 14:40:12 -0500296 if (asyncResp)
297 {
298 redfish::messages::internalError(asyncResp->res);
299 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500300 });
Ed Tanousa3e65892021-09-16 14:13:20 -0700301 task::Payload payload(req);
302 auto callback = [asyncResp,
303 payload](sdbusplus::message::message& m) mutable {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500304 BMCWEB_LOG_DEBUG << "Match fired";
Ed Tanousa3e65892021-09-16 14:13:20 -0700305 softwareInterfaceAdded(asyncResp, m, std::move(payload));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500306 };
307
308 fwUpdateInProgress = true;
309
310 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
311 *crow::connections::systemBus,
312 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
313 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
314 callback);
James Feist4cde5d92020-06-11 10:39:55 -0700315
316 fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match::match>(
317 *crow::connections::systemBus,
318 "type='signal',member='PropertiesChanged',path_namespace='/xyz/"
319 "openbmc_project/logging/entry',"
320 "arg0='xyz.openbmc_project.Logging.Entry'",
321 [asyncResp, url](sdbusplus::message::message& m) {
322 BMCWEB_LOG_DEBUG << "Error Match fired";
Ed Tanous168e20c2021-12-13 14:39:53 -0800323 boost::container::flat_map<std::string,
324 dbus::utility::DbusVariantType>
James Feist4cde5d92020-06-11 10:39:55 -0700325 values;
326 std::string objName;
327 m.read(objName, values);
328 auto find = values.find("Message");
329 if (find == values.end())
330 {
331 return;
332 }
333 std::string* type = std::get_if<std::string>(&(find->second));
334 if (type == nullptr)
335 {
336 return; // if this was our message, timeout will cover it
337 }
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600338 if (!boost::starts_with(*type, "xyz.openbmc_project.Software"))
James Feist4cde5d92020-06-11 10:39:55 -0700339 {
340 return;
341 }
342 if (*type ==
343 "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
344 {
345 redfish::messages::invalidUpload(asyncResp->res, url,
346 "Invalid archive");
347 }
George Liu0fda0f12021-11-16 10:06:17 +0800348 else if (
349 *type ==
350 "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure")
James Feist4cde5d92020-06-11 10:39:55 -0700351 {
352 redfish::messages::invalidUpload(asyncResp->res, url,
353 "Invalid manifest");
354 }
355 else if (*type ==
356 "xyz.openbmc_project.Software.Image.Error.ImageFailure")
357 {
358 redfish::messages::invalidUpload(asyncResp->res, url,
359 "Invalid image format");
360 }
George Liu0fda0f12021-11-16 10:06:17 +0800361 else if (*type ==
362 "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600363 {
364
365 redfish::messages::invalidUpload(
366 asyncResp->res, url, "Image version already exists");
367
368 redfish::messages::resourceAlreadyExists(
Chicago Duan0588a3b2021-06-10 18:20:36 +0800369 asyncResp->res, "UpdateService.v1_5_0.UpdateService",
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600370 "Version", "uploaded version");
371 }
James Feist4cde5d92020-06-11 10:39:55 -0700372 else if (*type ==
373 "xyz.openbmc_project.Software.Image.Error.BusyFailure")
374 {
375 redfish::messages::resourceExhaustion(asyncResp->res, url);
376 }
377 else
378 {
379 redfish::messages::internalError(asyncResp->res);
380 }
381 fwAvailableTimer = nullptr;
382 });
Andrew Geissler86adcd62019-04-18 10:58:05 -0500383}
Jennifer Lee729dae72018-04-24 15:59:34 -0700384
Andrew Geissler0554c982019-04-23 14:40:12 -0500385/**
386 * UpdateServiceActionsSimpleUpdate class supports handle POST method for
387 * SimpleUpdate action.
388 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700389inline void requestRoutesUpdateServiceActionsSimpleUpdate(App& app)
Andrew Geissler0554c982019-04-23 14:40:12 -0500390{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700391 BMCWEB_ROUTE(
392 app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
Ed Tanoused398212021-06-09 17:05:54 -0700393 .privileges(redfish::privileges::postUpdateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700394 .methods(
395 boost::beast::http::verb::
396 post)([](const crow::Request& req,
397 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
398 std::optional<std::string> transferProtocol;
399 std::string imageURI;
Andrew Geissler0554c982019-04-23 14:40:12 -0500400
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700401 BMCWEB_LOG_DEBUG << "Enter UpdateService.SimpleUpdate doPost";
Andrew Geissler0554c982019-04-23 14:40:12 -0500402
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700403 // User can pass in both TransferProtocol and ImageURI parameters or
404 // they can pass in just the ImageURI with the transfer protocol
405 // embedded within it.
406 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
407 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
Andrew Geissler0554c982019-04-23 14:40:12 -0500408
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700409 if (!json_util::readJson(req, asyncResp->res, "TransferProtocol",
410 transferProtocol, "ImageURI", imageURI))
411 {
412 BMCWEB_LOG_DEBUG
413 << "Missing TransferProtocol or ImageURI parameter";
414 return;
415 }
416 if (!transferProtocol)
417 {
418 // Must be option 2
419 // Verify ImageURI has transfer protocol in it
420 size_t separator = imageURI.find(':');
421 if ((separator == std::string::npos) ||
422 ((separator + 1) > imageURI.size()))
423 {
424 messages::actionParameterValueTypeError(
425 asyncResp->res, imageURI, "ImageURI",
426 "UpdateService.SimpleUpdate");
427 BMCWEB_LOG_ERROR << "ImageURI missing transfer protocol: "
428 << imageURI;
429 return;
430 }
431 transferProtocol = imageURI.substr(0, separator);
432 // Ensure protocol is upper case for a common comparison path
433 // below
434 boost::to_upper(*transferProtocol);
435 BMCWEB_LOG_DEBUG << "Encoded transfer protocol "
436 << *transferProtocol;
Andrew Geissler0554c982019-04-23 14:40:12 -0500437
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700438 // Adjust imageURI to not have the protocol on it for parsing
439 // below
440 // ex. tftp://1.1.1.1/myfile.bin -> 1.1.1.1/myfile.bin
441 imageURI = imageURI.substr(separator + 3);
442 BMCWEB_LOG_DEBUG << "Adjusted imageUri " << imageURI;
443 }
444
445 // OpenBMC currently only supports TFTP
446 if (*transferProtocol != "TFTP")
447 {
448 messages::actionParameterNotSupported(
449 asyncResp->res, "TransferProtocol",
450 "UpdateService.SimpleUpdate");
451 BMCWEB_LOG_ERROR << "Request incorrect protocol parameter: "
452 << *transferProtocol;
453 return;
454 }
455
456 // Format should be <IP or Hostname>/<file> for imageURI
457 size_t separator = imageURI.find('/');
Andrew Geissler0554c982019-04-23 14:40:12 -0500458 if ((separator == std::string::npos) ||
459 ((separator + 1) > imageURI.size()))
460 {
461 messages::actionParameterValueTypeError(
462 asyncResp->res, imageURI, "ImageURI",
463 "UpdateService.SimpleUpdate");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700464 BMCWEB_LOG_ERROR << "Invalid ImageURI: " << imageURI;
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530465 return;
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530466 }
467
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700468 std::string tftpServer = imageURI.substr(0, separator);
469 std::string fwFile = imageURI.substr(separator + 1);
470 BMCWEB_LOG_DEBUG << "Server: " << tftpServer + " File: " << fwFile;
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530471
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700472 // Setup callback for when new software detected
473 // Give TFTP 10 minutes to complete
474 monitorForSoftwareAvailable(
475 asyncResp, req,
476 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
477 600);
478
479 // TFTP can take up to 10 minutes depending on image size and
480 // connection speed. Return to caller as soon as the TFTP operation
481 // has been started. The callback above will ensure the activate
482 // is started once the download has completed
483 redfish::messages::success(asyncResp->res);
484
485 // Call TFTP service
486 crow::connections::systemBus->async_method_call(
487 [](const boost::system::error_code ec) {
488 if (ec)
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530489 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700490 // messages::internalError(asyncResp->res);
491 cleanUp();
492 BMCWEB_LOG_DEBUG << "error_code = " << ec;
493 BMCWEB_LOG_DEBUG << "error msg = " << ec.message();
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530494 }
495 else
496 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700497 BMCWEB_LOG_DEBUG << "Call to DownloaViaTFTP Success";
498 }
499 },
500 "xyz.openbmc_project.Software.Download",
501 "/xyz/openbmc_project/software",
502 "xyz.openbmc_project.Common.TFTP", "DownloadViaTFTP", fwFile,
503 tftpServer);
504
505 BMCWEB_LOG_DEBUG << "Exit UpdateService.SimpleUpdate doPost";
506 });
507}
508
509inline void requestRoutesUpdateService(App& app)
510{
511 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700512 .privileges(redfish::privileges::getUpdateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700513 .methods(
514 boost::beast::http::verb::
515 get)([](const crow::Request&,
516 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
517 asyncResp->res.jsonValue["@odata.type"] =
Chicago Duan0588a3b2021-06-10 18:20:36 +0800518 "#UpdateService.v1_5_0.UpdateService";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700519 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
520 asyncResp->res.jsonValue["Id"] = "UpdateService";
521 asyncResp->res.jsonValue["Description"] =
522 "Service for Software Update";
523 asyncResp->res.jsonValue["Name"] = "Update Service";
524 asyncResp->res.jsonValue["HttpPushUri"] =
525 "/redfish/v1/UpdateService";
526 // UpdateService cannot be disabled
527 asyncResp->res.jsonValue["ServiceEnabled"] = true;
528 asyncResp->res.jsonValue["FirmwareInventory"] = {
529 {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}};
Tejas Patild61e5192021-06-04 15:49:35 +0530530 // Get the MaxImageSizeBytes
531 asyncResp->res.jsonValue["MaxImageSizeBytes"] =
532 bmcwebHttpReqBodyLimitMb * 1024 * 1024;
533
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700534#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
535 // Update Actions object.
536 nlohmann::json& updateSvcSimpleUpdate =
537 asyncResp->res
538 .jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
539 updateSvcSimpleUpdate["target"] =
540 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
541 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] =
542 {"TFTP"};
543#endif
544 // Get the current ApplyTime value
545 crow::connections::systemBus->async_method_call(
546 [asyncResp](const boost::system::error_code ec,
Ed Tanous168e20c2021-12-13 14:39:53 -0800547 const dbus::utility::DbusVariantType& applyTime) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700548 if (ec)
549 {
550 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
551 messages::internalError(asyncResp->res);
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530552 return;
553 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530554
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700555 const std::string* s = std::get_if<std::string>(&applyTime);
556 if (s == nullptr)
557 {
558 return;
559 }
560 // Store the ApplyTime Value
George Liu0fda0f12021-11-16 10:06:17 +0800561 if (*s ==
562 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate")
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700563 {
564 asyncResp->res
565 .jsonValue["HttpPushUriOptions"]
566 ["HttpPushUriApplyTime"]["ApplyTime"] =
567 "Immediate";
568 }
George Liu0fda0f12021-11-16 10:06:17 +0800569 else if (
570 *s ==
571 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset")
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700572 {
573 asyncResp->res
574 .jsonValue["HttpPushUriOptions"]
575 ["HttpPushUriApplyTime"]["ApplyTime"] =
576 "OnReset";
577 }
578 },
579 "xyz.openbmc_project.Settings",
580 "/xyz/openbmc_project/software/apply_time",
581 "org.freedesktop.DBus.Properties", "Get",
582 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime");
583 });
584 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700585 .privileges(redfish::privileges::patchUpdateService)
George Liu0fda0f12021-11-16 10:06:17 +0800586 .methods(
587 boost::beast::http::verb::
588 patch)([](const crow::Request& req,
589 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
590 BMCWEB_LOG_DEBUG << "doPatch...";
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530591
George Liu0fda0f12021-11-16 10:06:17 +0800592 std::optional<nlohmann::json> pushUriOptions;
593 if (!json_util::readJson(req, asyncResp->res, "HttpPushUriOptions",
594 pushUriOptions))
595 {
596 return;
597 }
598
599 if (pushUriOptions)
600 {
601 std::optional<nlohmann::json> pushUriApplyTime;
602 if (!json_util::readJson(*pushUriOptions, asyncResp->res,
603 "HttpPushUriApplyTime",
604 pushUriApplyTime))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700605 {
Ed Tanousc711bf82018-07-30 16:31:33 -0700606 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700607 }
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700608
George Liu0fda0f12021-11-16 10:06:17 +0800609 if (pushUriApplyTime)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700610 {
George Liu0fda0f12021-11-16 10:06:17 +0800611 std::optional<std::string> applyTime;
612 if (!json_util::readJson(*pushUriApplyTime, asyncResp->res,
613 "ApplyTime", applyTime))
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700614 {
615 return;
616 }
617
George Liu0fda0f12021-11-16 10:06:17 +0800618 if (applyTime)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700619 {
George Liu0fda0f12021-11-16 10:06:17 +0800620 std::string applyTimeNewVal;
621 if (applyTime == "Immediate")
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700622 {
George Liu0fda0f12021-11-16 10:06:17 +0800623 applyTimeNewVal =
624 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
625 }
626 else if (applyTime == "OnReset")
627 {
628 applyTimeNewVal =
629 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
630 }
631 else
632 {
633 BMCWEB_LOG_INFO
634 << "ApplyTime value is not in the list of acceptable values";
635 messages::propertyValueNotInList(
636 asyncResp->res, *applyTime, "ApplyTime");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700637 return;
638 }
639
George Liu0fda0f12021-11-16 10:06:17 +0800640 // Set the requested image apply time value
641 crow::connections::systemBus->async_method_call(
642 [asyncResp](const boost::system::error_code ec) {
643 if (ec)
644 {
645 BMCWEB_LOG_ERROR
646 << "D-Bus responses error: " << ec;
647 messages::internalError(asyncResp->res);
648 return;
649 }
650 messages::success(asyncResp->res);
651 },
652 "xyz.openbmc_project.Settings",
653 "/xyz/openbmc_project/software/apply_time",
654 "org.freedesktop.DBus.Properties", "Set",
655 "xyz.openbmc_project.Software.ApplyTime",
656 "RequestedApplyTime",
Ed Tanous168e20c2021-12-13 14:39:53 -0800657 dbus::utility::DbusVariantType{applyTimeNewVal});
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700658 }
659 }
George Liu0fda0f12021-11-16 10:06:17 +0800660 }
661 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700662 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700663 .privileges(redfish::privileges::postUpdateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700664 .methods(boost::beast::http::verb::post)(
665 [](const crow::Request& req,
666 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
667 BMCWEB_LOG_DEBUG << "doPost...";
668
669 // Setup callback for when new software detected
670 monitorForSoftwareAvailable(asyncResp, req,
671 "/redfish/v1/UpdateService");
672
673 std::string filepath("/tmp/images/" +
674 boost::uuids::to_string(
675 boost::uuids::random_generator()()));
676 BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
677 std::ofstream out(filepath, std::ofstream::out |
678 std::ofstream::binary |
679 std::ofstream::trunc);
680 out << req.body;
681 out.close();
682 BMCWEB_LOG_DEBUG << "file upload complete!!";
683 });
684}
685
686inline void requestRoutesSoftwareInventoryCollection(App& app)
687{
688 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
Ed Tanoused398212021-06-09 17:05:54 -0700689 .privileges(redfish::privileges::getSoftwareInventoryCollection)
George Liu0fda0f12021-11-16 10:06:17 +0800690 .methods(
691 boost::beast::http::verb::
692 get)([](const crow::Request&,
693 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
694 asyncResp->res.jsonValue["@odata.type"] =
695 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
696 asyncResp->res.jsonValue["@odata.id"] =
697 "/redfish/v1/UpdateService/FirmwareInventory";
698 asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700699
George Liu0fda0f12021-11-16 10:06:17 +0800700 crow::connections::systemBus->async_method_call(
701 [asyncResp](
702 const boost::system::error_code ec,
703 const std::vector<
704 std::pair<std::string,
705 std::vector<std::pair<
706 std::string, std::vector<std::string>>>>>&
707 subtree) {
708 if (ec)
709 {
710 messages::internalError(asyncResp->res);
711 return;
712 }
713 asyncResp->res.jsonValue["Members"] =
714 nlohmann::json::array();
715 asyncResp->res.jsonValue["Members@odata.count"] = 0;
716
717 for (auto& obj : subtree)
718 {
719 sdbusplus::message::object_path path(obj.first);
720 std::string swId = path.filename();
721 if (swId.empty())
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700722 {
723 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +0800724 BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700725 return;
726 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700727
George Liu0fda0f12021-11-16 10:06:17 +0800728 nlohmann::json& members =
729 asyncResp->res.jsonValue["Members"];
730 members.push_back(
731 {{"@odata.id",
732 "/redfish/v1/UpdateService/FirmwareInventory/" +
733 swId}});
734 asyncResp->res.jsonValue["Members@odata.count"] =
735 members.size();
736 }
737 },
738 // Note that only firmware levels associated with a device
739 // are stored under /xyz/openbmc_project/software therefore
740 // to ensure only real FirmwareInventory items are returned,
741 // this full object path must be used here as input to
742 // mapper
743 "xyz.openbmc_project.ObjectMapper",
744 "/xyz/openbmc_project/object_mapper",
745 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
746 "/xyz/openbmc_project/software", static_cast<int32_t>(0),
747 std::array<const char*, 1>{
748 "xyz.openbmc_project.Software.Version"});
749 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700750}
751/* Fill related item links (i.e. bmc, bios) in for inventory */
752inline static void
753 getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
754 const std::string& purpose)
755{
756 if (purpose == fw_util::bmcPurpose)
757 {
758 nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
759 relatedItem.push_back({{"@odata.id", "/redfish/v1/Managers/bmc"}});
760 aResp->res.jsonValue["RelatedItem@odata.count"] = relatedItem.size();
761 }
762 else if (purpose == fw_util::biosPurpose)
763 {
764 nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
765 relatedItem.push_back(
766 {{"@odata.id", "/redfish/v1/Systems/system/Bios"}});
767 aResp->res.jsonValue["Members@odata.count"] = relatedItem.size();
768 }
769 else
770 {
771 BMCWEB_LOG_ERROR << "Unknown software purpose " << purpose;
772 }
773}
774
775inline void requestRoutesSoftwareInventory(App& app)
776{
777 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700778 .privileges(redfish::privileges::getSoftwareInventory)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700779 .methods(
780 boost::beast::http::verb::get)([](const crow::Request&,
781 const std::shared_ptr<
782 bmcweb::AsyncResp>& asyncResp,
783 const std::string& param) {
784 std::shared_ptr<std::string> swId =
785 std::make_shared<std::string>(param);
786
787 asyncResp->res.jsonValue["@odata.id"] =
788 "/redfish/v1/UpdateService/FirmwareInventory/" + *swId;
789
790 crow::connections::systemBus->async_method_call(
791 [asyncResp, swId](
792 const boost::system::error_code ec,
793 const std::vector<
794 std::pair<std::string,
795 std::vector<std::pair<
796 std::string, std::vector<std::string>>>>>&
797 subtree) {
798 BMCWEB_LOG_DEBUG << "doGet callback...";
799 if (ec)
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700800 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700801 messages::internalError(asyncResp->res);
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700802 return;
803 }
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700804
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700805 // Ensure we find our input swId, otherwise return an error
806 bool found = false;
807 for (const std::pair<
808 std::string,
809 std::vector<std::pair<
810 std::string, std::vector<std::string>>>>& obj :
811 subtree)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700812 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700813 if (boost::ends_with(obj.first, *swId) != true)
814 {
815 continue;
816 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700817
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700818 if (obj.second.size() < 1)
819 {
820 continue;
821 }
822
823 found = true;
824 fw_util::getFwStatus(asyncResp, swId,
825 obj.second[0].first);
826
827 crow::connections::systemBus->async_method_call(
Ed Tanous168e20c2021-12-13 14:39:53 -0800828 [asyncResp,
829 swId](const boost::system::error_code errorCode,
830 const boost::container::flat_map<
831 std::string,
832 dbus::utility::DbusVariantType>&
833 propertiesList) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700834 if (errorCode)
835 {
836 messages::internalError(asyncResp->res);
837 return;
838 }
839 boost::container::flat_map<
Ed Tanous168e20c2021-12-13 14:39:53 -0800840 std::string,
841 dbus::utility::DbusVariantType>::
842 const_iterator it =
843 propertiesList.find("Purpose");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700844 if (it == propertiesList.end())
845 {
846 BMCWEB_LOG_DEBUG
847 << "Can't find property \"Purpose\"!";
848 messages::propertyMissing(asyncResp->res,
849 "Purpose");
850 return;
851 }
852 const std::string* swInvPurpose =
853 std::get_if<std::string>(&it->second);
854 if (swInvPurpose == nullptr)
855 {
George Liu0fda0f12021-11-16 10:06:17 +0800856 BMCWEB_LOG_DEBUG
857 << "wrong types for property\"Purpose\"!";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700858 messages::propertyValueTypeError(
859 asyncResp->res, "", "Purpose");
860 return;
861 }
862
863 BMCWEB_LOG_DEBUG << "swInvPurpose = "
864 << *swInvPurpose;
865 it = propertiesList.find("Version");
866 if (it == propertiesList.end())
867 {
868 BMCWEB_LOG_DEBUG
869 << "Can't find property \"Version\"!";
870 messages::propertyMissing(asyncResp->res,
871 "Version");
872 return;
873 }
874
875 BMCWEB_LOG_DEBUG << "Version found!";
876
877 const std::string* version =
878 std::get_if<std::string>(&it->second);
879
880 if (version == nullptr)
881 {
882 BMCWEB_LOG_DEBUG
883 << "Can't find property \"Version\"!";
884
885 messages::propertyValueTypeError(
886 asyncResp->res, "", "Version");
887 return;
888 }
889 asyncResp->res.jsonValue["Version"] = *version;
890 asyncResp->res.jsonValue["Id"] = *swId;
891
892 // swInvPurpose is of format:
893 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
894 // Translate this to "ABC image"
895 size_t endDesc = swInvPurpose->rfind('.');
896 if (endDesc == std::string::npos)
897 {
898 messages::internalError(asyncResp->res);
899 return;
900 }
901 endDesc++;
902 if (endDesc >= swInvPurpose->size())
903 {
904 messages::internalError(asyncResp->res);
905 return;
906 }
907
908 std::string formatDesc =
909 swInvPurpose->substr(endDesc);
910 asyncResp->res.jsonValue["Description"] =
911 formatDesc + " image";
912 getRelatedItems(asyncResp, *swInvPurpose);
913 },
914 obj.second[0].first, obj.first,
915 "org.freedesktop.DBus.Properties", "GetAll",
916 "xyz.openbmc_project.Software.Version");
917 }
918 if (!found)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700919 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700920 BMCWEB_LOG_ERROR
921 << "Input swID " + *swId + " not found!";
922 messages::resourceMissingAtURI(
923 asyncResp->res,
924 "/redfish/v1/UpdateService/FirmwareInventory/" +
925 *swId);
926 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700927 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700928 asyncResp->res.jsonValue["@odata.type"] =
929 "#SoftwareInventory.v1_1_0.SoftwareInventory";
930 asyncResp->res.jsonValue["Name"] = "Software Inventory";
931 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700932
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700933 asyncResp->res.jsonValue["Updateable"] = false;
934 fw_util::getFwUpdateableStatus(asyncResp, swId);
935 },
936 "xyz.openbmc_project.ObjectMapper",
937 "/xyz/openbmc_project/object_mapper",
938 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/",
939 static_cast<int32_t>(0),
940 std::array<const char*, 1>{
941 "xyz.openbmc_project.Software.Version"});
942 });
943}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700944
945} // namespace redfish