blob: 7ca078701b53b2b2e43d1f80acefdefd42ffa305 [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>
Andrew Geissler87d84722019-02-28 14:28:39 -060022#include <utils/fw_utils.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050023
Ed Tanousabf2add2019-01-22 16:40:12 -080024#include <variant>
Jennifer Lee729dae72018-04-24 15:59:34 -070025
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",
57 std::variant<std::string>(
58 "xyz.openbmc_project.Software.Activation.RequestedActivations."
59 "Active"));
60}
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,
67 const crow::Request& req)
Andrew Geissler86adcd62019-04-18 10:58:05 -050068{
69 std::vector<std::pair<
70 std::string,
71 std::vector<std::pair<std::string, std::variant<std::string>>>>>
72 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(
James Feistfe306722020-03-12 16:32:08 -070087 [objPath, asyncResp,
Ed Tanous81ce6092020-12-17 16:54:55 +000088 req](const boost::system::error_code errorCode,
James Feistfe306722020-03-12 16:32:08 -070089 const std::vector<std::pair<
Gunnar Mills1214b7e2020-06-04 10:11:30 -050090 std::string, std::vector<std::string>>>& objInfo) {
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,
137 std::variant<std::string, uint8_t>>
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
James Feistfd9ab9e2020-05-19 13:48:07 -0700144 if (iface == "xyz.openbmc_project.Software."
145 "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 }
James Feistfd9ab9e2020-05-19 13:48:07 -0700200 else if (iface ==
201 "xyz.openbmc_project.Software."
202 "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 },
239 "type='signal',interface='org.freedesktop.DBus."
240 "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);
James Feistfe306722020-03-12 16:32:08 -0700245 task->payload.emplace(req);
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
266 if (fwUpdateInProgress != false)
267 {
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 });
302
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500303 auto callback = [asyncResp, req](sdbusplus::message::message& m) {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500304 BMCWEB_LOG_DEBUG << "Match fired";
James Feistfe306722020-03-12 16:32:08 -0700305 softwareInterfaceAdded(asyncResp, m, req);
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";
323 boost::container::flat_map<std::string, std::variant<std::string>>
324 values;
325 std::string objName;
326 m.read(objName, values);
327 auto find = values.find("Message");
328 if (find == values.end())
329 {
330 return;
331 }
332 std::string* type = std::get_if<std::string>(&(find->second));
333 if (type == nullptr)
334 {
335 return; // if this was our message, timeout will cover it
336 }
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600337 if (!boost::starts_with(*type, "xyz.openbmc_project.Software"))
James Feist4cde5d92020-06-11 10:39:55 -0700338 {
339 return;
340 }
341 if (*type ==
342 "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
343 {
344 redfish::messages::invalidUpload(asyncResp->res, url,
345 "Invalid archive");
346 }
347 else if (*type == "xyz.openbmc_project.Software.Image.Error."
348 "ManifestFileFailure")
349 {
350 redfish::messages::invalidUpload(asyncResp->res, url,
351 "Invalid manifest");
352 }
353 else if (*type ==
354 "xyz.openbmc_project.Software.Image.Error.ImageFailure")
355 {
356 redfish::messages::invalidUpload(asyncResp->res, url,
357 "Invalid image format");
358 }
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600359 else if (*type == "xyz.openbmc_project.Software.Version.Error."
360 "AlreadyExists")
361 {
362
363 redfish::messages::invalidUpload(
364 asyncResp->res, url, "Image version already exists");
365
366 redfish::messages::resourceAlreadyExists(
367 asyncResp->res, "UpdateService.v1_4_0.UpdateService",
368 "Version", "uploaded version");
369 }
James Feist4cde5d92020-06-11 10:39:55 -0700370 else if (*type ==
371 "xyz.openbmc_project.Software.Image.Error.BusyFailure")
372 {
373 redfish::messages::resourceExhaustion(asyncResp->res, url);
374 }
375 else
376 {
377 redfish::messages::internalError(asyncResp->res);
378 }
379 fwAvailableTimer = nullptr;
380 });
Andrew Geissler86adcd62019-04-18 10:58:05 -0500381}
Jennifer Lee729dae72018-04-24 15:59:34 -0700382
Andrew Geissler0554c982019-04-23 14:40:12 -0500383/**
384 * UpdateServiceActionsSimpleUpdate class supports handle POST method for
385 * SimpleUpdate action.
386 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700387inline void requestRoutesUpdateServiceActionsSimpleUpdate(App& app)
Andrew Geissler0554c982019-04-23 14:40:12 -0500388{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700389 BMCWEB_ROUTE(
390 app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
391 .privileges({"ConfigureComponents"})
392 .methods(
393 boost::beast::http::verb::
394 post)([](const crow::Request& req,
395 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
396 std::optional<std::string> transferProtocol;
397 std::string imageURI;
Andrew Geissler0554c982019-04-23 14:40:12 -0500398
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700399 BMCWEB_LOG_DEBUG << "Enter UpdateService.SimpleUpdate doPost";
Andrew Geissler0554c982019-04-23 14:40:12 -0500400
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700401 // User can pass in both TransferProtocol and ImageURI parameters or
402 // they can pass in just the ImageURI with the transfer protocol
403 // embedded within it.
404 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
405 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
Andrew Geissler0554c982019-04-23 14:40:12 -0500406
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700407 if (!json_util::readJson(req, asyncResp->res, "TransferProtocol",
408 transferProtocol, "ImageURI", imageURI))
409 {
410 BMCWEB_LOG_DEBUG
411 << "Missing TransferProtocol or ImageURI parameter";
412 return;
413 }
414 if (!transferProtocol)
415 {
416 // Must be option 2
417 // Verify ImageURI has transfer protocol in it
418 size_t separator = imageURI.find(':');
419 if ((separator == std::string::npos) ||
420 ((separator + 1) > imageURI.size()))
421 {
422 messages::actionParameterValueTypeError(
423 asyncResp->res, imageURI, "ImageURI",
424 "UpdateService.SimpleUpdate");
425 BMCWEB_LOG_ERROR << "ImageURI missing transfer protocol: "
426 << imageURI;
427 return;
428 }
429 transferProtocol = imageURI.substr(0, separator);
430 // Ensure protocol is upper case for a common comparison path
431 // below
432 boost::to_upper(*transferProtocol);
433 BMCWEB_LOG_DEBUG << "Encoded transfer protocol "
434 << *transferProtocol;
Andrew Geissler0554c982019-04-23 14:40:12 -0500435
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700436 // Adjust imageURI to not have the protocol on it for parsing
437 // below
438 // ex. tftp://1.1.1.1/myfile.bin -> 1.1.1.1/myfile.bin
439 imageURI = imageURI.substr(separator + 3);
440 BMCWEB_LOG_DEBUG << "Adjusted imageUri " << imageURI;
441 }
442
443 // OpenBMC currently only supports TFTP
444 if (*transferProtocol != "TFTP")
445 {
446 messages::actionParameterNotSupported(
447 asyncResp->res, "TransferProtocol",
448 "UpdateService.SimpleUpdate");
449 BMCWEB_LOG_ERROR << "Request incorrect protocol parameter: "
450 << *transferProtocol;
451 return;
452 }
453
454 // Format should be <IP or Hostname>/<file> for imageURI
455 size_t separator = imageURI.find('/');
Andrew Geissler0554c982019-04-23 14:40:12 -0500456 if ((separator == std::string::npos) ||
457 ((separator + 1) > imageURI.size()))
458 {
459 messages::actionParameterValueTypeError(
460 asyncResp->res, imageURI, "ImageURI",
461 "UpdateService.SimpleUpdate");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700462 BMCWEB_LOG_ERROR << "Invalid ImageURI: " << imageURI;
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530463 return;
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530464 }
465
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700466 std::string tftpServer = imageURI.substr(0, separator);
467 std::string fwFile = imageURI.substr(separator + 1);
468 BMCWEB_LOG_DEBUG << "Server: " << tftpServer + " File: " << fwFile;
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530469
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700470 // Setup callback for when new software detected
471 // Give TFTP 10 minutes to complete
472 monitorForSoftwareAvailable(
473 asyncResp, req,
474 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
475 600);
476
477 // TFTP can take up to 10 minutes depending on image size and
478 // connection speed. Return to caller as soon as the TFTP operation
479 // has been started. The callback above will ensure the activate
480 // is started once the download has completed
481 redfish::messages::success(asyncResp->res);
482
483 // Call TFTP service
484 crow::connections::systemBus->async_method_call(
485 [](const boost::system::error_code ec) {
486 if (ec)
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530487 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700488 // messages::internalError(asyncResp->res);
489 cleanUp();
490 BMCWEB_LOG_DEBUG << "error_code = " << ec;
491 BMCWEB_LOG_DEBUG << "error msg = " << ec.message();
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530492 }
493 else
494 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700495 BMCWEB_LOG_DEBUG << "Call to DownloaViaTFTP Success";
496 }
497 },
498 "xyz.openbmc_project.Software.Download",
499 "/xyz/openbmc_project/software",
500 "xyz.openbmc_project.Common.TFTP", "DownloadViaTFTP", fwFile,
501 tftpServer);
502
503 BMCWEB_LOG_DEBUG << "Exit UpdateService.SimpleUpdate doPost";
504 });
505}
506
507inline void requestRoutesUpdateService(App& app)
508{
509 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
510 .privileges({"Login"})
511 .methods(
512 boost::beast::http::verb::
513 get)([](const crow::Request&,
514 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
515 asyncResp->res.jsonValue["@odata.type"] =
516 "#UpdateService.v1_4_0.UpdateService";
517 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
518 asyncResp->res.jsonValue["Id"] = "UpdateService";
519 asyncResp->res.jsonValue["Description"] =
520 "Service for Software Update";
521 asyncResp->res.jsonValue["Name"] = "Update Service";
522 asyncResp->res.jsonValue["HttpPushUri"] =
523 "/redfish/v1/UpdateService";
524 // UpdateService cannot be disabled
525 asyncResp->res.jsonValue["ServiceEnabled"] = true;
526 asyncResp->res.jsonValue["FirmwareInventory"] = {
527 {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}};
Tejas Patild61e5192021-06-04 15:49:35 +0530528 // Get the MaxImageSizeBytes
529 asyncResp->res.jsonValue["MaxImageSizeBytes"] =
530 bmcwebHttpReqBodyLimitMb * 1024 * 1024;
531
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700532#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
533 // Update Actions object.
534 nlohmann::json& updateSvcSimpleUpdate =
535 asyncResp->res
536 .jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
537 updateSvcSimpleUpdate["target"] =
538 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
539 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] =
540 {"TFTP"};
541#endif
542 // Get the current ApplyTime value
543 crow::connections::systemBus->async_method_call(
544 [asyncResp](const boost::system::error_code ec,
545 const std::variant<std::string>& applyTime) {
546 if (ec)
547 {
548 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
549 messages::internalError(asyncResp->res);
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530550 return;
551 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530552
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700553 const std::string* s = std::get_if<std::string>(&applyTime);
554 if (s == nullptr)
555 {
556 return;
557 }
558 // Store the ApplyTime Value
559 if (*s == "xyz.openbmc_project.Software.ApplyTime."
560 "RequestedApplyTimes.Immediate")
561 {
562 asyncResp->res
563 .jsonValue["HttpPushUriOptions"]
564 ["HttpPushUriApplyTime"]["ApplyTime"] =
565 "Immediate";
566 }
567 else if (*s == "xyz.openbmc_project.Software.ApplyTime."
568 "RequestedApplyTimes.OnReset")
569 {
570 asyncResp->res
571 .jsonValue["HttpPushUriOptions"]
572 ["HttpPushUriApplyTime"]["ApplyTime"] =
573 "OnReset";
574 }
575 },
576 "xyz.openbmc_project.Settings",
577 "/xyz/openbmc_project/software/apply_time",
578 "org.freedesktop.DBus.Properties", "Get",
579 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime");
580 });
581 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
582 .privileges({"ConfigureComponents"})
583 .methods(boost::beast::http::verb::patch)(
584 [](const crow::Request& req,
585 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
586 BMCWEB_LOG_DEBUG << "doPatch...";
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530587
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700588 std::optional<nlohmann::json> pushUriOptions;
589 if (!json_util::readJson(req, asyncResp->res,
590 "HttpPushUriOptions", pushUriOptions))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700591 {
Ed Tanousc711bf82018-07-30 16:31:33 -0700592 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700593 }
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700594
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700595 if (pushUriOptions)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700596 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700597 std::optional<nlohmann::json> pushUriApplyTime;
598 if (!json_util::readJson(*pushUriOptions, asyncResp->res,
599 "HttpPushUriApplyTime",
600 pushUriApplyTime))
601 {
602 return;
603 }
604
605 if (pushUriApplyTime)
606 {
607 std::optional<std::string> applyTime;
608 if (!json_util::readJson(*pushUriApplyTime,
609 asyncResp->res, "ApplyTime",
610 applyTime))
611 {
612 return;
613 }
614
615 if (applyTime)
616 {
617 std::string applyTimeNewVal;
618 if (applyTime == "Immediate")
619 {
620 applyTimeNewVal =
621 "xyz.openbmc_project.Software.ApplyTime."
622 "RequestedApplyTimes.Immediate";
623 }
624 else if (applyTime == "OnReset")
625 {
626 applyTimeNewVal =
627 "xyz.openbmc_project.Software.ApplyTime."
628 "RequestedApplyTimes.OnReset";
629 }
630 else
631 {
632 BMCWEB_LOG_INFO
633 << "ApplyTime value is not in the list of "
634 "acceptable values";
635 messages::propertyValueNotInList(
636 asyncResp->res, *applyTime, "ApplyTime");
637 return;
638 }
639
640 // Set the requested image apply time value
641 crow::connections::systemBus->async_method_call(
642 [asyncResp](
643 const boost::system::error_code ec) {
644 if (ec)
645 {
646 BMCWEB_LOG_ERROR
647 << "D-Bus responses error: " << ec;
648 messages::internalError(asyncResp->res);
649 return;
650 }
651 messages::success(asyncResp->res);
652 },
653 "xyz.openbmc_project.Settings",
654 "/xyz/openbmc_project/software/apply_time",
655 "org.freedesktop.DBus.Properties", "Set",
656 "xyz.openbmc_project.Software.ApplyTime",
657 "RequestedApplyTime",
658 std::variant<std::string>{applyTimeNewVal});
659 }
660 }
661 }
662 });
663 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
664 .privileges({"ConfigureComponents"})
665 .methods(boost::beast::http::verb::post)(
666 [](const crow::Request& req,
667 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
668 BMCWEB_LOG_DEBUG << "doPost...";
669
670 // Setup callback for when new software detected
671 monitorForSoftwareAvailable(asyncResp, req,
672 "/redfish/v1/UpdateService");
673
674 std::string filepath("/tmp/images/" +
675 boost::uuids::to_string(
676 boost::uuids::random_generator()()));
677 BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
678 std::ofstream out(filepath, std::ofstream::out |
679 std::ofstream::binary |
680 std::ofstream::trunc);
681 out << req.body;
682 out.close();
683 BMCWEB_LOG_DEBUG << "file upload complete!!";
684 });
685}
686
687inline void requestRoutesSoftwareInventoryCollection(App& app)
688{
689 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
690 .privileges({"Login"})
691 .methods(boost::beast::http::verb::get)(
692 [](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"] =
699 "Software Inventory Collection";
700
701 crow::connections::systemBus->async_method_call(
702 [asyncResp](
703 const boost::system::error_code ec,
704 const std::vector<std::pair<
705 std::string,
706 std::vector<std::pair<std::string,
707 std::vector<std::string>>>>>&
708 subtree) {
709 if (ec)
710 {
711 messages::internalError(asyncResp->res);
712 return;
713 }
714 asyncResp->res.jsonValue["Members"] =
715 nlohmann::json::array();
716 asyncResp->res.jsonValue["Members@odata.count"] = 0;
717
718 for (auto& obj : subtree)
719 {
720 sdbusplus::message::object_path path(obj.first);
721 std::string swId = path.filename();
722 if (swId.empty())
723 {
724 messages::internalError(asyncResp->res);
725 BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
726 return;
727 }
728
729 nlohmann::json& members =
730 asyncResp->res.jsonValue["Members"];
731 members.push_back(
732 {{"@odata.id", "/redfish/v1/UpdateService/"
733 "FirmwareInventory/" +
734 swId}});
735 asyncResp->res.jsonValue["Members@odata.count"] =
736 members.size();
737 }
738 },
739 // Note that only firmware levels associated with a device
740 // are stored under /xyz/openbmc_project/software therefore
741 // to ensure only real FirmwareInventory items are returned,
742 // this full object path must be used here as input to
743 // mapper
744 "xyz.openbmc_project.ObjectMapper",
745 "/xyz/openbmc_project/object_mapper",
746 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
747 "/xyz/openbmc_project/software", static_cast<int32_t>(0),
748 std::array<const char*, 1>{
749 "xyz.openbmc_project.Software.Version"});
750 });
751}
752/* Fill related item links (i.e. bmc, bios) in for inventory */
753inline static void
754 getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
755 const std::string& purpose)
756{
757 if (purpose == fw_util::bmcPurpose)
758 {
759 nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
760 relatedItem.push_back({{"@odata.id", "/redfish/v1/Managers/bmc"}});
761 aResp->res.jsonValue["RelatedItem@odata.count"] = relatedItem.size();
762 }
763 else if (purpose == fw_util::biosPurpose)
764 {
765 nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
766 relatedItem.push_back(
767 {{"@odata.id", "/redfish/v1/Systems/system/Bios"}});
768 aResp->res.jsonValue["Members@odata.count"] = relatedItem.size();
769 }
770 else
771 {
772 BMCWEB_LOG_ERROR << "Unknown software purpose " << purpose;
773 }
774}
775
776inline void requestRoutesSoftwareInventory(App& app)
777{
778 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
779 .privileges({"Login"})
780 .methods(
781 boost::beast::http::verb::get)([](const crow::Request&,
782 const std::shared_ptr<
783 bmcweb::AsyncResp>& asyncResp,
784 const std::string& param) {
785 std::shared_ptr<std::string> swId =
786 std::make_shared<std::string>(param);
787
788 asyncResp->res.jsonValue["@odata.id"] =
789 "/redfish/v1/UpdateService/FirmwareInventory/" + *swId;
790
791 crow::connections::systemBus->async_method_call(
792 [asyncResp, swId](
793 const boost::system::error_code ec,
794 const std::vector<
795 std::pair<std::string,
796 std::vector<std::pair<
797 std::string, std::vector<std::string>>>>>&
798 subtree) {
799 BMCWEB_LOG_DEBUG << "doGet callback...";
800 if (ec)
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700801 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700802 messages::internalError(asyncResp->res);
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700803 return;
804 }
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700805
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700806 // Ensure we find our input swId, otherwise return an error
807 bool found = false;
808 for (const std::pair<
809 std::string,
810 std::vector<std::pair<
811 std::string, std::vector<std::string>>>>& obj :
812 subtree)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700813 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700814 if (boost::ends_with(obj.first, *swId) != true)
815 {
816 continue;
817 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700818
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700819 if (obj.second.size() < 1)
820 {
821 continue;
822 }
823
824 found = true;
825 fw_util::getFwStatus(asyncResp, swId,
826 obj.second[0].first);
827
828 crow::connections::systemBus->async_method_call(
829 [asyncResp, swId](
830 const boost::system::error_code errorCode,
831 const boost::container::flat_map<
832 std::string, VariantType>& propertiesList) {
833 if (errorCode)
834 {
835 messages::internalError(asyncResp->res);
836 return;
837 }
838 boost::container::flat_map<
839 std::string, VariantType>::const_iterator
840 it = propertiesList.find("Purpose");
841 if (it == propertiesList.end())
842 {
843 BMCWEB_LOG_DEBUG
844 << "Can't find property \"Purpose\"!";
845 messages::propertyMissing(asyncResp->res,
846 "Purpose");
847 return;
848 }
849 const std::string* swInvPurpose =
850 std::get_if<std::string>(&it->second);
851 if (swInvPurpose == nullptr)
852 {
853 BMCWEB_LOG_DEBUG << "wrong types for "
854 "property\"Purpose\"!";
855 messages::propertyValueTypeError(
856 asyncResp->res, "", "Purpose");
857 return;
858 }
859
860 BMCWEB_LOG_DEBUG << "swInvPurpose = "
861 << *swInvPurpose;
862 it = propertiesList.find("Version");
863 if (it == propertiesList.end())
864 {
865 BMCWEB_LOG_DEBUG
866 << "Can't find property \"Version\"!";
867 messages::propertyMissing(asyncResp->res,
868 "Version");
869 return;
870 }
871
872 BMCWEB_LOG_DEBUG << "Version found!";
873
874 const std::string* version =
875 std::get_if<std::string>(&it->second);
876
877 if (version == nullptr)
878 {
879 BMCWEB_LOG_DEBUG
880 << "Can't find property \"Version\"!";
881
882 messages::propertyValueTypeError(
883 asyncResp->res, "", "Version");
884 return;
885 }
886 asyncResp->res.jsonValue["Version"] = *version;
887 asyncResp->res.jsonValue["Id"] = *swId;
888
889 // swInvPurpose is of format:
890 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
891 // Translate this to "ABC image"
892 size_t endDesc = swInvPurpose->rfind('.');
893 if (endDesc == std::string::npos)
894 {
895 messages::internalError(asyncResp->res);
896 return;
897 }
898 endDesc++;
899 if (endDesc >= swInvPurpose->size())
900 {
901 messages::internalError(asyncResp->res);
902 return;
903 }
904
905 std::string formatDesc =
906 swInvPurpose->substr(endDesc);
907 asyncResp->res.jsonValue["Description"] =
908 formatDesc + " image";
909 getRelatedItems(asyncResp, *swInvPurpose);
910 },
911 obj.second[0].first, obj.first,
912 "org.freedesktop.DBus.Properties", "GetAll",
913 "xyz.openbmc_project.Software.Version");
914 }
915 if (!found)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700916 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700917 BMCWEB_LOG_ERROR
918 << "Input swID " + *swId + " not found!";
919 messages::resourceMissingAtURI(
920 asyncResp->res,
921 "/redfish/v1/UpdateService/FirmwareInventory/" +
922 *swId);
923 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700924 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700925 asyncResp->res.jsonValue["@odata.type"] =
926 "#SoftwareInventory.v1_1_0.SoftwareInventory";
927 asyncResp->res.jsonValue["Name"] = "Software Inventory";
928 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700929
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700930 asyncResp->res.jsonValue["Updateable"] = false;
931 fw_util::getFwUpdateableStatus(asyncResp, swId);
932 },
933 "xyz.openbmc_project.ObjectMapper",
934 "/xyz/openbmc_project/object_mapper",
935 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/",
936 static_cast<int32_t>(0),
937 std::array<const char*, 1>{
938 "xyz.openbmc_project.Software.Version"});
939 });
940}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700941
942} // namespace redfish