blob: bfab6fbe1fa154585abff9a2dc34e7e9db98f846 [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
John Edward Broadbent7e860f12021-04-08 15:57:16 -070018#include <app.hpp>
Jennifer Lee729dae72018-04-24 15:59:34 -070019#include <boost/container/flat_map.hpp>
Andrew Geissler87d84722019-02-28 14:28:39 -060020#include <utils/fw_utils.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050021
Ed Tanousabf2add2019-01-22 16:40:12 -080022#include <variant>
Jennifer Lee729dae72018-04-24 15:59:34 -070023
Ed Tanous1abe55e2018-09-05 08:30:59 -070024namespace redfish
25{
Ed Tanous27826b52018-10-29 11:40:58 -070026
Andrew Geissler0e7de462019-03-04 19:11:54 -060027// Match signals added on software path
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070028static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher;
James Feist4cde5d92020-06-11 10:39:55 -070029static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateErrorMatcher;
Andrew Geissler0e7de462019-03-04 19:11:54 -060030// Only allow one update at a time
31static bool fwUpdateInProgress = false;
Andrew Geissler86adcd62019-04-18 10:58:05 -050032// Timer for software available
Ed Tanous271584a2019-07-09 16:24:22 -070033static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
Andrew Geissler86adcd62019-04-18 10:58:05 -050034
John Edward Broadbent7e860f12021-04-08 15:57:16 -070035inline static void cleanUp()
Andrew Geissler86adcd62019-04-18 10:58:05 -050036{
37 fwUpdateInProgress = false;
38 fwUpdateMatcher = nullptr;
James Feist4cde5d92020-06-11 10:39:55 -070039 fwUpdateErrorMatcher = nullptr;
Andrew Geissler86adcd62019-04-18 10:58:05 -050040}
John Edward Broadbent7e860f12021-04-08 15:57:16 -070041inline static void activateImage(const std::string& objPath,
42 const std::string& service)
Andrew Geissler86adcd62019-04-18 10:58:05 -050043{
44 BMCWEB_LOG_DEBUG << "Activate image for " << objPath << " " << service;
45 crow::connections::systemBus->async_method_call(
Ed Tanous81ce6092020-12-17 16:54:55 +000046 [](const boost::system::error_code errorCode) {
47 if (errorCode)
Andrew Geissler86adcd62019-04-18 10:58:05 -050048 {
Ed Tanous81ce6092020-12-17 16:54:55 +000049 BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
50 BMCWEB_LOG_DEBUG << "error msg = " << errorCode.message();
Andrew Geissler86adcd62019-04-18 10:58:05 -050051 }
52 },
53 service, objPath, "org.freedesktop.DBus.Properties", "Set",
54 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
55 std::variant<std::string>(
56 "xyz.openbmc_project.Software.Activation.RequestedActivations."
57 "Active"));
58}
Andrew Geissler0554c982019-04-23 14:40:12 -050059
60// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
61// then no asyncResp updates will occur
zhanghch058d1b46d2021-04-01 11:18:24 +080062static void
63 softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
64 sdbusplus::message::message& m,
65 const crow::Request& req)
Andrew Geissler86adcd62019-04-18 10:58:05 -050066{
67 std::vector<std::pair<
68 std::string,
69 std::vector<std::pair<std::string, std::variant<std::string>>>>>
70 interfacesProperties;
71
72 sdbusplus::message::object_path objPath;
73
74 m.read(objPath, interfacesProperties);
75
76 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
Gunnar Mills1214b7e2020-06-04 10:11:30 -050077 for (auto& interface : interfacesProperties)
Andrew Geissler86adcd62019-04-18 10:58:05 -050078 {
79 BMCWEB_LOG_DEBUG << "interface = " << interface.first;
80
81 if (interface.first == "xyz.openbmc_project.Software.Activation")
82 {
Andrew Geissler86adcd62019-04-18 10:58:05 -050083 // Retrieve service and activate
84 crow::connections::systemBus->async_method_call(
James Feistfe306722020-03-12 16:32:08 -070085 [objPath, asyncResp,
Ed Tanous81ce6092020-12-17 16:54:55 +000086 req](const boost::system::error_code errorCode,
James Feistfe306722020-03-12 16:32:08 -070087 const std::vector<std::pair<
Gunnar Mills1214b7e2020-06-04 10:11:30 -050088 std::string, std::vector<std::string>>>& objInfo) {
Ed Tanous81ce6092020-12-17 16:54:55 +000089 if (errorCode)
Andrew Geissler86adcd62019-04-18 10:58:05 -050090 {
Ed Tanous81ce6092020-12-17 16:54:55 +000091 BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
Andrew Geissler86adcd62019-04-18 10:58:05 -050092 BMCWEB_LOG_DEBUG << "error msg = "
Ed Tanous81ce6092020-12-17 16:54:55 +000093 << errorCode.message();
Andrew Geissler0554c982019-04-23 14:40:12 -050094 if (asyncResp)
95 {
96 messages::internalError(asyncResp->res);
97 }
Andrew Geissler86adcd62019-04-18 10:58:05 -050098 cleanUp();
99 return;
100 }
101 // Ensure we only got one service back
102 if (objInfo.size() != 1)
103 {
104 BMCWEB_LOG_ERROR << "Invalid Object Size "
105 << objInfo.size();
Andrew Geissler0554c982019-04-23 14:40:12 -0500106 if (asyncResp)
107 {
108 messages::internalError(asyncResp->res);
109 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500110 cleanUp();
111 return;
112 }
113 // cancel timer only when
114 // xyz.openbmc_project.Software.Activation interface
115 // is added
116 fwAvailableTimer = nullptr;
117
118 activateImage(objPath.str, objInfo[0].first);
Andrew Geissler0554c982019-04-23 14:40:12 -0500119 if (asyncResp)
120 {
James Feist32898ce2020-03-10 16:16:52 -0700121 std::shared_ptr<task::TaskData> task =
122 task::TaskData::createTask(
123 [](boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500124 sdbusplus::message::message& msg,
125 const std::shared_ptr<task::TaskData>&
126 taskData) {
James Feist32898ce2020-03-10 16:16:52 -0700127 if (ec)
128 {
129 return task::completed;
130 }
131
132 std::string iface;
133 boost::container::flat_map<
James Feistfd9ab9e2020-05-19 13:48:07 -0700134 std::string,
135 std::variant<std::string, uint8_t>>
James Feist32898ce2020-03-10 16:16:52 -0700136 values;
James Feist32898ce2020-03-10 16:16:52 -0700137
James Feiste5d50062020-05-11 17:29:00 -0700138 std::string index =
139 std::to_string(taskData->index);
James Feistfd9ab9e2020-05-19 13:48:07 -0700140 msg.read(iface, values);
James Feiste5d50062020-05-11 17:29:00 -0700141
James Feistfd9ab9e2020-05-19 13:48:07 -0700142 if (iface == "xyz.openbmc_project.Software."
143 "Activation")
James Feist32898ce2020-03-10 16:16:52 -0700144 {
James Feistfd9ab9e2020-05-19 13:48:07 -0700145 auto findActivation =
146 values.find("Activation");
147 if (findActivation == values.end())
148 {
149 return !task::completed;
150 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500151 std::string* state =
James Feistfd9ab9e2020-05-19 13:48:07 -0700152 std::get_if<std::string>(
153 &(findActivation->second));
154
155 if (state == nullptr)
156 {
157 taskData->messages.emplace_back(
158 messages::internalError());
159 return task::completed;
160 }
161
162 if (boost::ends_with(*state,
163 "Invalid") ||
164 boost::ends_with(*state, "Failed"))
165 {
166 taskData->state = "Exception";
167 taskData->status = "Warning";
168 taskData->messages.emplace_back(
169 messages::taskAborted(index));
170 return task::completed;
171 }
172
173 if (boost::ends_with(*state, "Staged"))
174 {
175 taskData->state = "Stopping";
176 taskData->messages.emplace_back(
177 messages::taskPaused(index));
178
179 // its staged, set a long timer to
180 // allow them time to complete the
181 // update (probably cycle the
182 // system) if this expires then
183 // task will be cancelled
184 taskData->extendTimer(
185 std::chrono::hours(5));
186 return !task::completed;
187 }
188
189 if (boost::ends_with(*state, "Active"))
190 {
191 taskData->messages.emplace_back(
192 messages::taskCompletedOK(
193 index));
194 taskData->state = "Completed";
195 return task::completed;
196 }
James Feist32898ce2020-03-10 16:16:52 -0700197 }
James Feistfd9ab9e2020-05-19 13:48:07 -0700198 else if (iface ==
199 "xyz.openbmc_project.Software."
200 "ActivationProgress")
James Feist32898ce2020-03-10 16:16:52 -0700201 {
James Feistfd9ab9e2020-05-19 13:48:07 -0700202 auto findProgress =
203 values.find("Progress");
204 if (findProgress == values.end())
205 {
206 return !task::completed;
207 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500208 uint8_t* progress =
James Feistfd9ab9e2020-05-19 13:48:07 -0700209 std::get_if<uint8_t>(
210 &(findProgress->second));
James Feist32898ce2020-03-10 16:16:52 -0700211
James Feistfd9ab9e2020-05-19 13:48:07 -0700212 if (progress == nullptr)
213 {
214 taskData->messages.emplace_back(
215 messages::internalError());
216 return task::completed;
217 }
George Liu6868ff52021-01-02 11:37:41 +0800218 taskData->percentComplete =
219 static_cast<int>(*progress);
James Feist32898ce2020-03-10 16:16:52 -0700220 taskData->messages.emplace_back(
James Feistfd9ab9e2020-05-19 13:48:07 -0700221 messages::taskProgressChanged(
222 index, static_cast<size_t>(
223 *progress)));
224
225 // if we're getting status updates it's
226 // still alive, update timer
227 taskData->extendTimer(
228 std::chrono::minutes(5));
James Feist32898ce2020-03-10 16:16:52 -0700229 }
230
231 // as firmware update often results in a
232 // reboot, the task may never "complete"
233 // unless it is an error
234
235 return !task::completed;
236 },
237 "type='signal',interface='org.freedesktop.DBus."
238 "Properties',"
James Feistfd9ab9e2020-05-19 13:48:07 -0700239 "member='PropertiesChanged',path='" +
James Feist32898ce2020-03-10 16:16:52 -0700240 objPath.str + "'");
241 task->startTimer(std::chrono::minutes(5));
242 task->populateResp(asyncResp->res);
James Feistfe306722020-03-12 16:32:08 -0700243 task->payload.emplace(req);
Andrew Geissler0554c982019-04-23 14:40:12 -0500244 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500245 fwUpdateInProgress = false;
246 },
247 "xyz.openbmc_project.ObjectMapper",
248 "/xyz/openbmc_project/object_mapper",
249 "xyz.openbmc_project.ObjectMapper", "GetObject", objPath.str,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500250 std::array<const char*, 1>{
Andrew Geissler86adcd62019-04-18 10:58:05 -0500251 "xyz.openbmc_project.Software.Activation"});
252 }
253 }
254}
255
Andrew Geissler0554c982019-04-23 14:40:12 -0500256// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
257// then no asyncResp updates will occur
Ed Tanousb5a76932020-09-29 16:16:58 -0700258static void monitorForSoftwareAvailable(
zhanghch058d1b46d2021-04-01 11:18:24 +0800259 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
260 const crow::Request& req, const std::string& url,
261 int timeoutTimeSeconds = 10)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500262{
263 // Only allow one FW update at a time
264 if (fwUpdateInProgress != false)
265 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500266 if (asyncResp)
267 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500268 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
269 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500270 return;
271 }
272
Andrew Geissler0554c982019-04-23 14:40:12 -0500273 fwAvailableTimer =
Ed Tanous271584a2019-07-09 16:24:22 -0700274 std::make_unique<boost::asio::steady_timer>(*req.ioService);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500275
Ed Tanous271584a2019-07-09 16:24:22 -0700276 fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500277
278 fwAvailableTimer->async_wait(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500279 [asyncResp](const boost::system::error_code& ec) {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500280 cleanUp();
281 if (ec == boost::asio::error::operation_aborted)
282 {
283 // expected, we were canceled before the timer completed.
284 return;
285 }
286 BMCWEB_LOG_ERROR
287 << "Timed out waiting for firmware object being created";
288 BMCWEB_LOG_ERROR
289 << "FW image may has already been uploaded to server";
290 if (ec)
291 {
292 BMCWEB_LOG_ERROR << "Async_wait failed" << ec;
293 return;
294 }
Andrew Geissler0554c982019-04-23 14:40:12 -0500295 if (asyncResp)
296 {
297 redfish::messages::internalError(asyncResp->res);
298 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500299 });
300
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500301 auto callback = [asyncResp, req](sdbusplus::message::message& m) {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500302 BMCWEB_LOG_DEBUG << "Match fired";
James Feistfe306722020-03-12 16:32:08 -0700303 softwareInterfaceAdded(asyncResp, m, req);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500304 };
305
306 fwUpdateInProgress = true;
307
308 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
309 *crow::connections::systemBus,
310 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
311 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
312 callback);
James Feist4cde5d92020-06-11 10:39:55 -0700313
314 fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match::match>(
315 *crow::connections::systemBus,
316 "type='signal',member='PropertiesChanged',path_namespace='/xyz/"
317 "openbmc_project/logging/entry',"
318 "arg0='xyz.openbmc_project.Logging.Entry'",
319 [asyncResp, url](sdbusplus::message::message& m) {
320 BMCWEB_LOG_DEBUG << "Error Match fired";
321 boost::container::flat_map<std::string, std::variant<std::string>>
322 values;
323 std::string objName;
324 m.read(objName, values);
325 auto find = values.find("Message");
326 if (find == values.end())
327 {
328 return;
329 }
330 std::string* type = std::get_if<std::string>(&(find->second));
331 if (type == nullptr)
332 {
333 return; // if this was our message, timeout will cover it
334 }
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600335 if (!boost::starts_with(*type, "xyz.openbmc_project.Software"))
James Feist4cde5d92020-06-11 10:39:55 -0700336 {
337 return;
338 }
339 if (*type ==
340 "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
341 {
342 redfish::messages::invalidUpload(asyncResp->res, url,
343 "Invalid archive");
344 }
345 else if (*type == "xyz.openbmc_project.Software.Image.Error."
346 "ManifestFileFailure")
347 {
348 redfish::messages::invalidUpload(asyncResp->res, url,
349 "Invalid manifest");
350 }
351 else if (*type ==
352 "xyz.openbmc_project.Software.Image.Error.ImageFailure")
353 {
354 redfish::messages::invalidUpload(asyncResp->res, url,
355 "Invalid image format");
356 }
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600357 else if (*type == "xyz.openbmc_project.Software.Version.Error."
358 "AlreadyExists")
359 {
360
361 redfish::messages::invalidUpload(
362 asyncResp->res, url, "Image version already exists");
363
364 redfish::messages::resourceAlreadyExists(
365 asyncResp->res, "UpdateService.v1_4_0.UpdateService",
366 "Version", "uploaded version");
367 }
James Feist4cde5d92020-06-11 10:39:55 -0700368 else if (*type ==
369 "xyz.openbmc_project.Software.Image.Error.BusyFailure")
370 {
371 redfish::messages::resourceExhaustion(asyncResp->res, url);
372 }
373 else
374 {
375 redfish::messages::internalError(asyncResp->res);
376 }
377 fwAvailableTimer = nullptr;
378 });
Andrew Geissler86adcd62019-04-18 10:58:05 -0500379}
Jennifer Lee729dae72018-04-24 15:59:34 -0700380
Andrew Geissler0554c982019-04-23 14:40:12 -0500381/**
382 * UpdateServiceActionsSimpleUpdate class supports handle POST method for
383 * SimpleUpdate action.
384 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700385inline void requestRoutesUpdateServiceActionsSimpleUpdate(App& app)
Andrew Geissler0554c982019-04-23 14:40:12 -0500386{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700387 BMCWEB_ROUTE(
388 app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
389 .privileges({"ConfigureComponents"})
390 .methods(
391 boost::beast::http::verb::
392 post)([](const crow::Request& req,
393 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
394 std::optional<std::string> transferProtocol;
395 std::string imageURI;
Andrew Geissler0554c982019-04-23 14:40:12 -0500396
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700397 BMCWEB_LOG_DEBUG << "Enter UpdateService.SimpleUpdate doPost";
Andrew Geissler0554c982019-04-23 14:40:12 -0500398
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700399 // User can pass in both TransferProtocol and ImageURI parameters or
400 // they can pass in just the ImageURI with the transfer protocol
401 // embedded within it.
402 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
403 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
Andrew Geissler0554c982019-04-23 14:40:12 -0500404
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700405 if (!json_util::readJson(req, asyncResp->res, "TransferProtocol",
406 transferProtocol, "ImageURI", imageURI))
407 {
408 BMCWEB_LOG_DEBUG
409 << "Missing TransferProtocol or ImageURI parameter";
410 return;
411 }
412 if (!transferProtocol)
413 {
414 // Must be option 2
415 // Verify ImageURI has transfer protocol in it
416 size_t separator = imageURI.find(':');
417 if ((separator == std::string::npos) ||
418 ((separator + 1) > imageURI.size()))
419 {
420 messages::actionParameterValueTypeError(
421 asyncResp->res, imageURI, "ImageURI",
422 "UpdateService.SimpleUpdate");
423 BMCWEB_LOG_ERROR << "ImageURI missing transfer protocol: "
424 << imageURI;
425 return;
426 }
427 transferProtocol = imageURI.substr(0, separator);
428 // Ensure protocol is upper case for a common comparison path
429 // below
430 boost::to_upper(*transferProtocol);
431 BMCWEB_LOG_DEBUG << "Encoded transfer protocol "
432 << *transferProtocol;
Andrew Geissler0554c982019-04-23 14:40:12 -0500433
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700434 // Adjust imageURI to not have the protocol on it for parsing
435 // below
436 // ex. tftp://1.1.1.1/myfile.bin -> 1.1.1.1/myfile.bin
437 imageURI = imageURI.substr(separator + 3);
438 BMCWEB_LOG_DEBUG << "Adjusted imageUri " << imageURI;
439 }
440
441 // OpenBMC currently only supports TFTP
442 if (*transferProtocol != "TFTP")
443 {
444 messages::actionParameterNotSupported(
445 asyncResp->res, "TransferProtocol",
446 "UpdateService.SimpleUpdate");
447 BMCWEB_LOG_ERROR << "Request incorrect protocol parameter: "
448 << *transferProtocol;
449 return;
450 }
451
452 // Format should be <IP or Hostname>/<file> for imageURI
453 size_t separator = imageURI.find('/');
Andrew Geissler0554c982019-04-23 14:40:12 -0500454 if ((separator == std::string::npos) ||
455 ((separator + 1) > imageURI.size()))
456 {
457 messages::actionParameterValueTypeError(
458 asyncResp->res, imageURI, "ImageURI",
459 "UpdateService.SimpleUpdate");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700460 BMCWEB_LOG_ERROR << "Invalid ImageURI: " << imageURI;
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530461 return;
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530462 }
463
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700464 std::string tftpServer = imageURI.substr(0, separator);
465 std::string fwFile = imageURI.substr(separator + 1);
466 BMCWEB_LOG_DEBUG << "Server: " << tftpServer + " File: " << fwFile;
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530467
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700468 // Setup callback for when new software detected
469 // Give TFTP 10 minutes to complete
470 monitorForSoftwareAvailable(
471 asyncResp, req,
472 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
473 600);
474
475 // TFTP can take up to 10 minutes depending on image size and
476 // connection speed. Return to caller as soon as the TFTP operation
477 // has been started. The callback above will ensure the activate
478 // is started once the download has completed
479 redfish::messages::success(asyncResp->res);
480
481 // Call TFTP service
482 crow::connections::systemBus->async_method_call(
483 [](const boost::system::error_code ec) {
484 if (ec)
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530485 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700486 // messages::internalError(asyncResp->res);
487 cleanUp();
488 BMCWEB_LOG_DEBUG << "error_code = " << ec;
489 BMCWEB_LOG_DEBUG << "error msg = " << ec.message();
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530490 }
491 else
492 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700493 BMCWEB_LOG_DEBUG << "Call to DownloaViaTFTP Success";
494 }
495 },
496 "xyz.openbmc_project.Software.Download",
497 "/xyz/openbmc_project/software",
498 "xyz.openbmc_project.Common.TFTP", "DownloadViaTFTP", fwFile,
499 tftpServer);
500
501 BMCWEB_LOG_DEBUG << "Exit UpdateService.SimpleUpdate doPost";
502 });
503}
504
505inline void requestRoutesUpdateService(App& app)
506{
507 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
508 .privileges({"Login"})
509 .methods(
510 boost::beast::http::verb::
511 get)([](const crow::Request&,
512 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
513 asyncResp->res.jsonValue["@odata.type"] =
514 "#UpdateService.v1_4_0.UpdateService";
515 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
516 asyncResp->res.jsonValue["Id"] = "UpdateService";
517 asyncResp->res.jsonValue["Description"] =
518 "Service for Software Update";
519 asyncResp->res.jsonValue["Name"] = "Update Service";
520 asyncResp->res.jsonValue["HttpPushUri"] =
521 "/redfish/v1/UpdateService";
522 // UpdateService cannot be disabled
523 asyncResp->res.jsonValue["ServiceEnabled"] = true;
524 asyncResp->res.jsonValue["FirmwareInventory"] = {
525 {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}};
526#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
527 // Update Actions object.
528 nlohmann::json& updateSvcSimpleUpdate =
529 asyncResp->res
530 .jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
531 updateSvcSimpleUpdate["target"] =
532 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
533 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] =
534 {"TFTP"};
535#endif
536 // Get the current ApplyTime value
537 crow::connections::systemBus->async_method_call(
538 [asyncResp](const boost::system::error_code ec,
539 const std::variant<std::string>& applyTime) {
540 if (ec)
541 {
542 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
543 messages::internalError(asyncResp->res);
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530544 return;
545 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530546
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700547 const std::string* s = std::get_if<std::string>(&applyTime);
548 if (s == nullptr)
549 {
550 return;
551 }
552 // Store the ApplyTime Value
553 if (*s == "xyz.openbmc_project.Software.ApplyTime."
554 "RequestedApplyTimes.Immediate")
555 {
556 asyncResp->res
557 .jsonValue["HttpPushUriOptions"]
558 ["HttpPushUriApplyTime"]["ApplyTime"] =
559 "Immediate";
560 }
561 else if (*s == "xyz.openbmc_project.Software.ApplyTime."
562 "RequestedApplyTimes.OnReset")
563 {
564 asyncResp->res
565 .jsonValue["HttpPushUriOptions"]
566 ["HttpPushUriApplyTime"]["ApplyTime"] =
567 "OnReset";
568 }
569 },
570 "xyz.openbmc_project.Settings",
571 "/xyz/openbmc_project/software/apply_time",
572 "org.freedesktop.DBus.Properties", "Get",
573 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime");
574 });
575 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
576 .privileges({"ConfigureComponents"})
577 .methods(boost::beast::http::verb::patch)(
578 [](const crow::Request& req,
579 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
580 BMCWEB_LOG_DEBUG << "doPatch...";
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530581
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700582 std::optional<nlohmann::json> pushUriOptions;
583 if (!json_util::readJson(req, asyncResp->res,
584 "HttpPushUriOptions", pushUriOptions))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700585 {
Ed Tanousc711bf82018-07-30 16:31:33 -0700586 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700587 }
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700588
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700589 if (pushUriOptions)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700590 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700591 std::optional<nlohmann::json> pushUriApplyTime;
592 if (!json_util::readJson(*pushUriOptions, asyncResp->res,
593 "HttpPushUriApplyTime",
594 pushUriApplyTime))
595 {
596 return;
597 }
598
599 if (pushUriApplyTime)
600 {
601 std::optional<std::string> applyTime;
602 if (!json_util::readJson(*pushUriApplyTime,
603 asyncResp->res, "ApplyTime",
604 applyTime))
605 {
606 return;
607 }
608
609 if (applyTime)
610 {
611 std::string applyTimeNewVal;
612 if (applyTime == "Immediate")
613 {
614 applyTimeNewVal =
615 "xyz.openbmc_project.Software.ApplyTime."
616 "RequestedApplyTimes.Immediate";
617 }
618 else if (applyTime == "OnReset")
619 {
620 applyTimeNewVal =
621 "xyz.openbmc_project.Software.ApplyTime."
622 "RequestedApplyTimes.OnReset";
623 }
624 else
625 {
626 BMCWEB_LOG_INFO
627 << "ApplyTime value is not in the list of "
628 "acceptable values";
629 messages::propertyValueNotInList(
630 asyncResp->res, *applyTime, "ApplyTime");
631 return;
632 }
633
634 // Set the requested image apply time value
635 crow::connections::systemBus->async_method_call(
636 [asyncResp](
637 const boost::system::error_code ec) {
638 if (ec)
639 {
640 BMCWEB_LOG_ERROR
641 << "D-Bus responses error: " << ec;
642 messages::internalError(asyncResp->res);
643 return;
644 }
645 messages::success(asyncResp->res);
646 },
647 "xyz.openbmc_project.Settings",
648 "/xyz/openbmc_project/software/apply_time",
649 "org.freedesktop.DBus.Properties", "Set",
650 "xyz.openbmc_project.Software.ApplyTime",
651 "RequestedApplyTime",
652 std::variant<std::string>{applyTimeNewVal});
653 }
654 }
655 }
656 });
657 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
658 .privileges({"ConfigureComponents"})
659 .methods(boost::beast::http::verb::post)(
660 [](const crow::Request& req,
661 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
662 BMCWEB_LOG_DEBUG << "doPost...";
663
664 // Setup callback for when new software detected
665 monitorForSoftwareAvailable(asyncResp, req,
666 "/redfish/v1/UpdateService");
667
668 std::string filepath("/tmp/images/" +
669 boost::uuids::to_string(
670 boost::uuids::random_generator()()));
671 BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
672 std::ofstream out(filepath, std::ofstream::out |
673 std::ofstream::binary |
674 std::ofstream::trunc);
675 out << req.body;
676 out.close();
677 BMCWEB_LOG_DEBUG << "file upload complete!!";
678 });
679}
680
681inline void requestRoutesSoftwareInventoryCollection(App& app)
682{
683 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
684 .privileges({"Login"})
685 .methods(boost::beast::http::verb::get)(
686 [](const crow::Request&,
687 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
688 asyncResp->res.jsonValue["@odata.type"] =
689 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
690 asyncResp->res.jsonValue["@odata.id"] =
691 "/redfish/v1/UpdateService/FirmwareInventory";
692 asyncResp->res.jsonValue["Name"] =
693 "Software Inventory Collection";
694
695 crow::connections::systemBus->async_method_call(
696 [asyncResp](
697 const boost::system::error_code ec,
698 const std::vector<std::pair<
699 std::string,
700 std::vector<std::pair<std::string,
701 std::vector<std::string>>>>>&
702 subtree) {
703 if (ec)
704 {
705 messages::internalError(asyncResp->res);
706 return;
707 }
708 asyncResp->res.jsonValue["Members"] =
709 nlohmann::json::array();
710 asyncResp->res.jsonValue["Members@odata.count"] = 0;
711
712 for (auto& obj : subtree)
713 {
714 sdbusplus::message::object_path path(obj.first);
715 std::string swId = path.filename();
716 if (swId.empty())
717 {
718 messages::internalError(asyncResp->res);
719 BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
720 return;
721 }
722
723 nlohmann::json& members =
724 asyncResp->res.jsonValue["Members"];
725 members.push_back(
726 {{"@odata.id", "/redfish/v1/UpdateService/"
727 "FirmwareInventory/" +
728 swId}});
729 asyncResp->res.jsonValue["Members@odata.count"] =
730 members.size();
731 }
732 },
733 // Note that only firmware levels associated with a device
734 // are stored under /xyz/openbmc_project/software therefore
735 // to ensure only real FirmwareInventory items are returned,
736 // this full object path must be used here as input to
737 // mapper
738 "xyz.openbmc_project.ObjectMapper",
739 "/xyz/openbmc_project/object_mapper",
740 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
741 "/xyz/openbmc_project/software", static_cast<int32_t>(0),
742 std::array<const char*, 1>{
743 "xyz.openbmc_project.Software.Version"});
744 });
745}
746/* Fill related item links (i.e. bmc, bios) in for inventory */
747inline static void
748 getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
749 const std::string& purpose)
750{
751 if (purpose == fw_util::bmcPurpose)
752 {
753 nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
754 relatedItem.push_back({{"@odata.id", "/redfish/v1/Managers/bmc"}});
755 aResp->res.jsonValue["RelatedItem@odata.count"] = relatedItem.size();
756 }
757 else if (purpose == fw_util::biosPurpose)
758 {
759 nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
760 relatedItem.push_back(
761 {{"@odata.id", "/redfish/v1/Systems/system/Bios"}});
762 aResp->res.jsonValue["Members@odata.count"] = relatedItem.size();
763 }
764 else
765 {
766 BMCWEB_LOG_ERROR << "Unknown software purpose " << purpose;
767 }
768}
769
770inline void requestRoutesSoftwareInventory(App& app)
771{
772 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
773 .privileges({"Login"})
774 .methods(
775 boost::beast::http::verb::get)([](const crow::Request&,
776 const std::shared_ptr<
777 bmcweb::AsyncResp>& asyncResp,
778 const std::string& param) {
779 std::shared_ptr<std::string> swId =
780 std::make_shared<std::string>(param);
781
782 asyncResp->res.jsonValue["@odata.id"] =
783 "/redfish/v1/UpdateService/FirmwareInventory/" + *swId;
784
785 crow::connections::systemBus->async_method_call(
786 [asyncResp, swId](
787 const boost::system::error_code ec,
788 const std::vector<
789 std::pair<std::string,
790 std::vector<std::pair<
791 std::string, std::vector<std::string>>>>>&
792 subtree) {
793 BMCWEB_LOG_DEBUG << "doGet callback...";
794 if (ec)
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700795 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700796 messages::internalError(asyncResp->res);
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700797 return;
798 }
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700799
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700800 // Ensure we find our input swId, otherwise return an error
801 bool found = false;
802 for (const std::pair<
803 std::string,
804 std::vector<std::pair<
805 std::string, std::vector<std::string>>>>& obj :
806 subtree)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700807 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700808 if (boost::ends_with(obj.first, *swId) != true)
809 {
810 continue;
811 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700812
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700813 if (obj.second.size() < 1)
814 {
815 continue;
816 }
817
818 found = true;
819 fw_util::getFwStatus(asyncResp, swId,
820 obj.second[0].first);
821
822 crow::connections::systemBus->async_method_call(
823 [asyncResp, swId](
824 const boost::system::error_code errorCode,
825 const boost::container::flat_map<
826 std::string, VariantType>& propertiesList) {
827 if (errorCode)
828 {
829 messages::internalError(asyncResp->res);
830 return;
831 }
832 boost::container::flat_map<
833 std::string, VariantType>::const_iterator
834 it = propertiesList.find("Purpose");
835 if (it == propertiesList.end())
836 {
837 BMCWEB_LOG_DEBUG
838 << "Can't find property \"Purpose\"!";
839 messages::propertyMissing(asyncResp->res,
840 "Purpose");
841 return;
842 }
843 const std::string* swInvPurpose =
844 std::get_if<std::string>(&it->second);
845 if (swInvPurpose == nullptr)
846 {
847 BMCWEB_LOG_DEBUG << "wrong types for "
848 "property\"Purpose\"!";
849 messages::propertyValueTypeError(
850 asyncResp->res, "", "Purpose");
851 return;
852 }
853
854 BMCWEB_LOG_DEBUG << "swInvPurpose = "
855 << *swInvPurpose;
856 it = propertiesList.find("Version");
857 if (it == propertiesList.end())
858 {
859 BMCWEB_LOG_DEBUG
860 << "Can't find property \"Version\"!";
861 messages::propertyMissing(asyncResp->res,
862 "Version");
863 return;
864 }
865
866 BMCWEB_LOG_DEBUG << "Version found!";
867
868 const std::string* version =
869 std::get_if<std::string>(&it->second);
870
871 if (version == nullptr)
872 {
873 BMCWEB_LOG_DEBUG
874 << "Can't find property \"Version\"!";
875
876 messages::propertyValueTypeError(
877 asyncResp->res, "", "Version");
878 return;
879 }
880 asyncResp->res.jsonValue["Version"] = *version;
881 asyncResp->res.jsonValue["Id"] = *swId;
882
883 // swInvPurpose is of format:
884 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
885 // Translate this to "ABC image"
886 size_t endDesc = swInvPurpose->rfind('.');
887 if (endDesc == std::string::npos)
888 {
889 messages::internalError(asyncResp->res);
890 return;
891 }
892 endDesc++;
893 if (endDesc >= swInvPurpose->size())
894 {
895 messages::internalError(asyncResp->res);
896 return;
897 }
898
899 std::string formatDesc =
900 swInvPurpose->substr(endDesc);
901 asyncResp->res.jsonValue["Description"] =
902 formatDesc + " image";
903 getRelatedItems(asyncResp, *swInvPurpose);
904 },
905 obj.second[0].first, obj.first,
906 "org.freedesktop.DBus.Properties", "GetAll",
907 "xyz.openbmc_project.Software.Version");
908 }
909 if (!found)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700910 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700911 BMCWEB_LOG_ERROR
912 << "Input swID " + *swId + " not found!";
913 messages::resourceMissingAtURI(
914 asyncResp->res,
915 "/redfish/v1/UpdateService/FirmwareInventory/" +
916 *swId);
917 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700918 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700919 asyncResp->res.jsonValue["@odata.type"] =
920 "#SoftwareInventory.v1_1_0.SoftwareInventory";
921 asyncResp->res.jsonValue["Name"] = "Software Inventory";
922 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700923
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700924 asyncResp->res.jsonValue["Updateable"] = false;
925 fw_util::getFwUpdateableStatus(asyncResp, swId);
926 },
927 "xyz.openbmc_project.ObjectMapper",
928 "/xyz/openbmc_project/object_mapper",
929 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/",
930 static_cast<int32_t>(0),
931 std::array<const char*, 1>{
932 "xyz.openbmc_project.Software.Version"});
933 });
934}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700935
936} // namespace redfish