blob: e420130df7004caafc9a674d34806a3ddb3ac3df [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 Tanoused398212021-06-09 17:05:54 -070022#include <registries/privilege_registry.hpp>
Andrew Geissler87d84722019-02-28 14:28:39 -060023#include <utils/fw_utils.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050024
Ed Tanousabf2add2019-01-22 16:40:12 -080025#include <variant>
Jennifer Lee729dae72018-04-24 15:59:34 -070026
Ed Tanous1abe55e2018-09-05 08:30:59 -070027namespace redfish
28{
Ed Tanous27826b52018-10-29 11:40:58 -070029
Andrew Geissler0e7de462019-03-04 19:11:54 -060030// Match signals added on software path
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070031static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher;
James Feist4cde5d92020-06-11 10:39:55 -070032static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateErrorMatcher;
Andrew Geissler0e7de462019-03-04 19:11:54 -060033// Only allow one update at a time
34static bool fwUpdateInProgress = false;
Andrew Geissler86adcd62019-04-18 10:58:05 -050035// Timer for software available
Ed Tanous271584a2019-07-09 16:24:22 -070036static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
Andrew Geissler86adcd62019-04-18 10:58:05 -050037
John Edward Broadbent7e860f12021-04-08 15:57:16 -070038inline static void cleanUp()
Andrew Geissler86adcd62019-04-18 10:58:05 -050039{
40 fwUpdateInProgress = false;
41 fwUpdateMatcher = nullptr;
James Feist4cde5d92020-06-11 10:39:55 -070042 fwUpdateErrorMatcher = nullptr;
Andrew Geissler86adcd62019-04-18 10:58:05 -050043}
John Edward Broadbent7e860f12021-04-08 15:57:16 -070044inline static void activateImage(const std::string& objPath,
45 const std::string& service)
Andrew Geissler86adcd62019-04-18 10:58:05 -050046{
47 BMCWEB_LOG_DEBUG << "Activate image for " << objPath << " " << service;
48 crow::connections::systemBus->async_method_call(
Ed Tanous81ce6092020-12-17 16:54:55 +000049 [](const boost::system::error_code errorCode) {
50 if (errorCode)
Andrew Geissler86adcd62019-04-18 10:58:05 -050051 {
Ed Tanous81ce6092020-12-17 16:54:55 +000052 BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
53 BMCWEB_LOG_DEBUG << "error msg = " << errorCode.message();
Andrew Geissler86adcd62019-04-18 10:58:05 -050054 }
55 },
56 service, objPath, "org.freedesktop.DBus.Properties", "Set",
57 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
58 std::variant<std::string>(
59 "xyz.openbmc_project.Software.Activation.RequestedActivations."
60 "Active"));
61}
Andrew Geissler0554c982019-04-23 14:40:12 -050062
63// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
64// then no asyncResp updates will occur
zhanghch058d1b46d2021-04-01 11:18:24 +080065static void
66 softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
67 sdbusplus::message::message& m,
Ed Tanousa3e65892021-09-16 14:13:20 -070068 task::Payload&& payload)
Andrew Geissler86adcd62019-04-18 10:58:05 -050069{
70 std::vector<std::pair<
71 std::string,
72 std::vector<std::pair<std::string, std::variant<std::string>>>>>
73 interfacesProperties;
74
75 sdbusplus::message::object_path objPath;
76
77 m.read(objPath, interfacesProperties);
78
79 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
Gunnar Mills1214b7e2020-06-04 10:11:30 -050080 for (auto& interface : interfacesProperties)
Andrew Geissler86adcd62019-04-18 10:58:05 -050081 {
82 BMCWEB_LOG_DEBUG << "interface = " << interface.first;
83
84 if (interface.first == "xyz.openbmc_project.Software.Activation")
85 {
Andrew Geissler86adcd62019-04-18 10:58:05 -050086 // Retrieve service and activate
87 crow::connections::systemBus->async_method_call(
Ed Tanousa3e65892021-09-16 14:13:20 -070088 [objPath, asyncResp, payload(std::move(payload))](
89 const boost::system::error_code errorCode,
90 const std::vector<
91 std::pair<std::string, std::vector<std::string>>>&
92 objInfo) mutable {
Ed Tanous81ce6092020-12-17 16:54:55 +000093 if (errorCode)
Andrew Geissler86adcd62019-04-18 10:58:05 -050094 {
Ed Tanous81ce6092020-12-17 16:54:55 +000095 BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
Andrew Geissler86adcd62019-04-18 10:58:05 -050096 BMCWEB_LOG_DEBUG << "error msg = "
Ed Tanous81ce6092020-12-17 16:54:55 +000097 << errorCode.message();
Andrew Geissler0554c982019-04-23 14:40:12 -050098 if (asyncResp)
99 {
100 messages::internalError(asyncResp->res);
101 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500102 cleanUp();
103 return;
104 }
105 // Ensure we only got one service back
106 if (objInfo.size() != 1)
107 {
108 BMCWEB_LOG_ERROR << "Invalid Object Size "
109 << objInfo.size();
Andrew Geissler0554c982019-04-23 14:40:12 -0500110 if (asyncResp)
111 {
112 messages::internalError(asyncResp->res);
113 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500114 cleanUp();
115 return;
116 }
117 // cancel timer only when
118 // xyz.openbmc_project.Software.Activation interface
119 // is added
120 fwAvailableTimer = nullptr;
121
122 activateImage(objPath.str, objInfo[0].first);
Andrew Geissler0554c982019-04-23 14:40:12 -0500123 if (asyncResp)
124 {
James Feist32898ce2020-03-10 16:16:52 -0700125 std::shared_ptr<task::TaskData> task =
126 task::TaskData::createTask(
127 [](boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500128 sdbusplus::message::message& msg,
129 const std::shared_ptr<task::TaskData>&
130 taskData) {
James Feist32898ce2020-03-10 16:16:52 -0700131 if (ec)
132 {
133 return task::completed;
134 }
135
136 std::string iface;
137 boost::container::flat_map<
James Feistfd9ab9e2020-05-19 13:48:07 -0700138 std::string,
139 std::variant<std::string, uint8_t>>
James Feist32898ce2020-03-10 16:16:52 -0700140 values;
James Feist32898ce2020-03-10 16:16:52 -0700141
James Feiste5d50062020-05-11 17:29:00 -0700142 std::string index =
143 std::to_string(taskData->index);
James Feistfd9ab9e2020-05-19 13:48:07 -0700144 msg.read(iface, values);
James Feiste5d50062020-05-11 17:29:00 -0700145
James Feistfd9ab9e2020-05-19 13:48:07 -0700146 if (iface == "xyz.openbmc_project.Software."
147 "Activation")
James Feist32898ce2020-03-10 16:16:52 -0700148 {
James Feistfd9ab9e2020-05-19 13:48:07 -0700149 auto findActivation =
150 values.find("Activation");
151 if (findActivation == values.end())
152 {
153 return !task::completed;
154 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500155 std::string* state =
James Feistfd9ab9e2020-05-19 13:48:07 -0700156 std::get_if<std::string>(
157 &(findActivation->second));
158
159 if (state == nullptr)
160 {
161 taskData->messages.emplace_back(
162 messages::internalError());
163 return task::completed;
164 }
165
166 if (boost::ends_with(*state,
167 "Invalid") ||
168 boost::ends_with(*state, "Failed"))
169 {
170 taskData->state = "Exception";
171 taskData->status = "Warning";
172 taskData->messages.emplace_back(
173 messages::taskAborted(index));
174 return task::completed;
175 }
176
177 if (boost::ends_with(*state, "Staged"))
178 {
179 taskData->state = "Stopping";
180 taskData->messages.emplace_back(
181 messages::taskPaused(index));
182
183 // its staged, set a long timer to
184 // allow them time to complete the
185 // update (probably cycle the
186 // system) if this expires then
187 // task will be cancelled
188 taskData->extendTimer(
189 std::chrono::hours(5));
190 return !task::completed;
191 }
192
193 if (boost::ends_with(*state, "Active"))
194 {
195 taskData->messages.emplace_back(
196 messages::taskCompletedOK(
197 index));
198 taskData->state = "Completed";
199 return task::completed;
200 }
James Feist32898ce2020-03-10 16:16:52 -0700201 }
James Feistfd9ab9e2020-05-19 13:48:07 -0700202 else if (iface ==
203 "xyz.openbmc_project.Software."
204 "ActivationProgress")
James Feist32898ce2020-03-10 16:16:52 -0700205 {
James Feistfd9ab9e2020-05-19 13:48:07 -0700206 auto findProgress =
207 values.find("Progress");
208 if (findProgress == values.end())
209 {
210 return !task::completed;
211 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500212 uint8_t* progress =
James Feistfd9ab9e2020-05-19 13:48:07 -0700213 std::get_if<uint8_t>(
214 &(findProgress->second));
James Feist32898ce2020-03-10 16:16:52 -0700215
James Feistfd9ab9e2020-05-19 13:48:07 -0700216 if (progress == nullptr)
217 {
218 taskData->messages.emplace_back(
219 messages::internalError());
220 return task::completed;
221 }
George Liu6868ff52021-01-02 11:37:41 +0800222 taskData->percentComplete =
223 static_cast<int>(*progress);
James Feist32898ce2020-03-10 16:16:52 -0700224 taskData->messages.emplace_back(
James Feistfd9ab9e2020-05-19 13:48:07 -0700225 messages::taskProgressChanged(
226 index, static_cast<size_t>(
227 *progress)));
228
229 // if we're getting status updates it's
230 // still alive, update timer
231 taskData->extendTimer(
232 std::chrono::minutes(5));
James Feist32898ce2020-03-10 16:16:52 -0700233 }
234
235 // as firmware update often results in a
236 // reboot, the task may never "complete"
237 // unless it is an error
238
239 return !task::completed;
240 },
241 "type='signal',interface='org.freedesktop.DBus."
242 "Properties',"
James Feistfd9ab9e2020-05-19 13:48:07 -0700243 "member='PropertiesChanged',path='" +
James Feist32898ce2020-03-10 16:16:52 -0700244 objPath.str + "'");
245 task->startTimer(std::chrono::minutes(5));
246 task->populateResp(asyncResp->res);
Ed Tanousa3e65892021-09-16 14:13:20 -0700247 task->payload.emplace(std::move(payload));
Andrew Geissler0554c982019-04-23 14:40:12 -0500248 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500249 fwUpdateInProgress = false;
250 },
251 "xyz.openbmc_project.ObjectMapper",
252 "/xyz/openbmc_project/object_mapper",
253 "xyz.openbmc_project.ObjectMapper", "GetObject", objPath.str,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500254 std::array<const char*, 1>{
Andrew Geissler86adcd62019-04-18 10:58:05 -0500255 "xyz.openbmc_project.Software.Activation"});
256 }
257 }
258}
259
Andrew Geissler0554c982019-04-23 14:40:12 -0500260// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
261// then no asyncResp updates will occur
Ed Tanousb5a76932020-09-29 16:16:58 -0700262static void monitorForSoftwareAvailable(
zhanghch058d1b46d2021-04-01 11:18:24 +0800263 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
264 const crow::Request& req, const std::string& url,
265 int timeoutTimeSeconds = 10)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500266{
267 // Only allow one FW update at a time
268 if (fwUpdateInProgress != false)
269 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500270 if (asyncResp)
271 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500272 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
273 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500274 return;
275 }
276
Andrew Geissler0554c982019-04-23 14:40:12 -0500277 fwAvailableTimer =
Ed Tanous271584a2019-07-09 16:24:22 -0700278 std::make_unique<boost::asio::steady_timer>(*req.ioService);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500279
Ed Tanous271584a2019-07-09 16:24:22 -0700280 fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500281
282 fwAvailableTimer->async_wait(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500283 [asyncResp](const boost::system::error_code& ec) {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500284 cleanUp();
285 if (ec == boost::asio::error::operation_aborted)
286 {
287 // expected, we were canceled before the timer completed.
288 return;
289 }
290 BMCWEB_LOG_ERROR
291 << "Timed out waiting for firmware object being created";
292 BMCWEB_LOG_ERROR
293 << "FW image may has already been uploaded to server";
294 if (ec)
295 {
296 BMCWEB_LOG_ERROR << "Async_wait failed" << ec;
297 return;
298 }
Andrew Geissler0554c982019-04-23 14:40:12 -0500299 if (asyncResp)
300 {
301 redfish::messages::internalError(asyncResp->res);
302 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500303 });
Ed Tanousa3e65892021-09-16 14:13:20 -0700304 task::Payload payload(req);
305 auto callback = [asyncResp,
306 payload](sdbusplus::message::message& m) mutable {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500307 BMCWEB_LOG_DEBUG << "Match fired";
Ed Tanousa3e65892021-09-16 14:13:20 -0700308 softwareInterfaceAdded(asyncResp, m, std::move(payload));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500309 };
310
311 fwUpdateInProgress = true;
312
313 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
314 *crow::connections::systemBus,
315 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
316 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
317 callback);
James Feist4cde5d92020-06-11 10:39:55 -0700318
319 fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match::match>(
320 *crow::connections::systemBus,
321 "type='signal',member='PropertiesChanged',path_namespace='/xyz/"
322 "openbmc_project/logging/entry',"
323 "arg0='xyz.openbmc_project.Logging.Entry'",
324 [asyncResp, url](sdbusplus::message::message& m) {
325 BMCWEB_LOG_DEBUG << "Error Match fired";
326 boost::container::flat_map<std::string, std::variant<std::string>>
327 values;
328 std::string objName;
329 m.read(objName, values);
330 auto find = values.find("Message");
331 if (find == values.end())
332 {
333 return;
334 }
335 std::string* type = std::get_if<std::string>(&(find->second));
336 if (type == nullptr)
337 {
338 return; // if this was our message, timeout will cover it
339 }
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600340 if (!boost::starts_with(*type, "xyz.openbmc_project.Software"))
James Feist4cde5d92020-06-11 10:39:55 -0700341 {
342 return;
343 }
344 if (*type ==
345 "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
346 {
347 redfish::messages::invalidUpload(asyncResp->res, url,
348 "Invalid archive");
349 }
350 else if (*type == "xyz.openbmc_project.Software.Image.Error."
351 "ManifestFileFailure")
352 {
353 redfish::messages::invalidUpload(asyncResp->res, url,
354 "Invalid manifest");
355 }
356 else if (*type ==
357 "xyz.openbmc_project.Software.Image.Error.ImageFailure")
358 {
359 redfish::messages::invalidUpload(asyncResp->res, url,
360 "Invalid image format");
361 }
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600362 else if (*type == "xyz.openbmc_project.Software.Version.Error."
363 "AlreadyExists")
364 {
365
366 redfish::messages::invalidUpload(
367 asyncResp->res, url, "Image version already exists");
368
369 redfish::messages::resourceAlreadyExists(
Chicago Duan0588a3b2021-06-10 18:20:36 +0800370 asyncResp->res, "UpdateService.v1_5_0.UpdateService",
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600371 "Version", "uploaded version");
372 }
James Feist4cde5d92020-06-11 10:39:55 -0700373 else if (*type ==
374 "xyz.openbmc_project.Software.Image.Error.BusyFailure")
375 {
376 redfish::messages::resourceExhaustion(asyncResp->res, url);
377 }
378 else
379 {
380 redfish::messages::internalError(asyncResp->res);
381 }
382 fwAvailableTimer = nullptr;
383 });
Andrew Geissler86adcd62019-04-18 10:58:05 -0500384}
Jennifer Lee729dae72018-04-24 15:59:34 -0700385
Andrew Geissler0554c982019-04-23 14:40:12 -0500386/**
387 * UpdateServiceActionsSimpleUpdate class supports handle POST method for
388 * SimpleUpdate action.
389 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700390inline void requestRoutesUpdateServiceActionsSimpleUpdate(App& app)
Andrew Geissler0554c982019-04-23 14:40:12 -0500391{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700392 BMCWEB_ROUTE(
393 app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
Ed Tanoused398212021-06-09 17:05:54 -0700394 .privileges(redfish::privileges::postUpdateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700395 .methods(
396 boost::beast::http::verb::
397 post)([](const crow::Request& req,
398 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
399 std::optional<std::string> transferProtocol;
400 std::string imageURI;
Andrew Geissler0554c982019-04-23 14:40:12 -0500401
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700402 BMCWEB_LOG_DEBUG << "Enter UpdateService.SimpleUpdate doPost";
Andrew Geissler0554c982019-04-23 14:40:12 -0500403
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700404 // User can pass in both TransferProtocol and ImageURI parameters or
405 // they can pass in just the ImageURI with the transfer protocol
406 // embedded within it.
407 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
408 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
Andrew Geissler0554c982019-04-23 14:40:12 -0500409
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700410 if (!json_util::readJson(req, asyncResp->res, "TransferProtocol",
411 transferProtocol, "ImageURI", imageURI))
412 {
413 BMCWEB_LOG_DEBUG
414 << "Missing TransferProtocol or ImageURI parameter";
415 return;
416 }
417 if (!transferProtocol)
418 {
419 // Must be option 2
420 // Verify ImageURI has transfer protocol in it
421 size_t separator = imageURI.find(':');
422 if ((separator == std::string::npos) ||
423 ((separator + 1) > imageURI.size()))
424 {
425 messages::actionParameterValueTypeError(
426 asyncResp->res, imageURI, "ImageURI",
427 "UpdateService.SimpleUpdate");
428 BMCWEB_LOG_ERROR << "ImageURI missing transfer protocol: "
429 << imageURI;
430 return;
431 }
432 transferProtocol = imageURI.substr(0, separator);
433 // Ensure protocol is upper case for a common comparison path
434 // below
435 boost::to_upper(*transferProtocol);
436 BMCWEB_LOG_DEBUG << "Encoded transfer protocol "
437 << *transferProtocol;
Andrew Geissler0554c982019-04-23 14:40:12 -0500438
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700439 // Adjust imageURI to not have the protocol on it for parsing
440 // below
441 // ex. tftp://1.1.1.1/myfile.bin -> 1.1.1.1/myfile.bin
442 imageURI = imageURI.substr(separator + 3);
443 BMCWEB_LOG_DEBUG << "Adjusted imageUri " << imageURI;
444 }
445
446 // OpenBMC currently only supports TFTP
447 if (*transferProtocol != "TFTP")
448 {
449 messages::actionParameterNotSupported(
450 asyncResp->res, "TransferProtocol",
451 "UpdateService.SimpleUpdate");
452 BMCWEB_LOG_ERROR << "Request incorrect protocol parameter: "
453 << *transferProtocol;
454 return;
455 }
456
457 // Format should be <IP or Hostname>/<file> for imageURI
458 size_t separator = imageURI.find('/');
Andrew Geissler0554c982019-04-23 14:40:12 -0500459 if ((separator == std::string::npos) ||
460 ((separator + 1) > imageURI.size()))
461 {
462 messages::actionParameterValueTypeError(
463 asyncResp->res, imageURI, "ImageURI",
464 "UpdateService.SimpleUpdate");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700465 BMCWEB_LOG_ERROR << "Invalid ImageURI: " << imageURI;
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530466 return;
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530467 }
468
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700469 std::string tftpServer = imageURI.substr(0, separator);
470 std::string fwFile = imageURI.substr(separator + 1);
471 BMCWEB_LOG_DEBUG << "Server: " << tftpServer + " File: " << fwFile;
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530472
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700473 // Setup callback for when new software detected
474 // Give TFTP 10 minutes to complete
475 monitorForSoftwareAvailable(
476 asyncResp, req,
477 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
478 600);
479
480 // TFTP can take up to 10 minutes depending on image size and
481 // connection speed. Return to caller as soon as the TFTP operation
482 // has been started. The callback above will ensure the activate
483 // is started once the download has completed
484 redfish::messages::success(asyncResp->res);
485
486 // Call TFTP service
487 crow::connections::systemBus->async_method_call(
488 [](const boost::system::error_code ec) {
489 if (ec)
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530490 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700491 // messages::internalError(asyncResp->res);
492 cleanUp();
493 BMCWEB_LOG_DEBUG << "error_code = " << ec;
494 BMCWEB_LOG_DEBUG << "error msg = " << ec.message();
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530495 }
496 else
497 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700498 BMCWEB_LOG_DEBUG << "Call to DownloaViaTFTP Success";
499 }
500 },
501 "xyz.openbmc_project.Software.Download",
502 "/xyz/openbmc_project/software",
503 "xyz.openbmc_project.Common.TFTP", "DownloadViaTFTP", fwFile,
504 tftpServer);
505
506 BMCWEB_LOG_DEBUG << "Exit UpdateService.SimpleUpdate doPost";
507 });
508}
509
510inline void requestRoutesUpdateService(App& app)
511{
512 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700513 .privileges(redfish::privileges::getUpdateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700514 .methods(
515 boost::beast::http::verb::
516 get)([](const crow::Request&,
517 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
518 asyncResp->res.jsonValue["@odata.type"] =
Chicago Duan0588a3b2021-06-10 18:20:36 +0800519 "#UpdateService.v1_5_0.UpdateService";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700520 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
521 asyncResp->res.jsonValue["Id"] = "UpdateService";
522 asyncResp->res.jsonValue["Description"] =
523 "Service for Software Update";
524 asyncResp->res.jsonValue["Name"] = "Update Service";
525 asyncResp->res.jsonValue["HttpPushUri"] =
526 "/redfish/v1/UpdateService";
527 // UpdateService cannot be disabled
528 asyncResp->res.jsonValue["ServiceEnabled"] = true;
529 asyncResp->res.jsonValue["FirmwareInventory"] = {
530 {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}};
Tejas Patild61e5192021-06-04 15:49:35 +0530531 // Get the MaxImageSizeBytes
532 asyncResp->res.jsonValue["MaxImageSizeBytes"] =
533 bmcwebHttpReqBodyLimitMb * 1024 * 1024;
534
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700535#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
536 // Update Actions object.
537 nlohmann::json& updateSvcSimpleUpdate =
538 asyncResp->res
539 .jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
540 updateSvcSimpleUpdate["target"] =
541 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
542 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] =
543 {"TFTP"};
544#endif
545 // Get the current ApplyTime value
546 crow::connections::systemBus->async_method_call(
547 [asyncResp](const boost::system::error_code ec,
548 const std::variant<std::string>& applyTime) {
549 if (ec)
550 {
551 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
552 messages::internalError(asyncResp->res);
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530553 return;
554 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530555
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700556 const std::string* s = std::get_if<std::string>(&applyTime);
557 if (s == nullptr)
558 {
559 return;
560 }
561 // Store the ApplyTime Value
562 if (*s == "xyz.openbmc_project.Software.ApplyTime."
563 "RequestedApplyTimes.Immediate")
564 {
565 asyncResp->res
566 .jsonValue["HttpPushUriOptions"]
567 ["HttpPushUriApplyTime"]["ApplyTime"] =
568 "Immediate";
569 }
570 else if (*s == "xyz.openbmc_project.Software.ApplyTime."
571 "RequestedApplyTimes.OnReset")
572 {
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)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700586 .methods(boost::beast::http::verb::patch)(
587 [](const crow::Request& req,
588 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
589 BMCWEB_LOG_DEBUG << "doPatch...";
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530590
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700591 std::optional<nlohmann::json> pushUriOptions;
592 if (!json_util::readJson(req, asyncResp->res,
593 "HttpPushUriOptions", pushUriOptions))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700594 {
Ed Tanousc711bf82018-07-30 16:31:33 -0700595 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700596 }
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700597
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700598 if (pushUriOptions)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700599 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700600 std::optional<nlohmann::json> pushUriApplyTime;
601 if (!json_util::readJson(*pushUriOptions, asyncResp->res,
602 "HttpPushUriApplyTime",
603 pushUriApplyTime))
604 {
605 return;
606 }
607
608 if (pushUriApplyTime)
609 {
610 std::optional<std::string> applyTime;
611 if (!json_util::readJson(*pushUriApplyTime,
612 asyncResp->res, "ApplyTime",
613 applyTime))
614 {
615 return;
616 }
617
618 if (applyTime)
619 {
620 std::string applyTimeNewVal;
621 if (applyTime == "Immediate")
622 {
623 applyTimeNewVal =
624 "xyz.openbmc_project.Software.ApplyTime."
625 "RequestedApplyTimes.Immediate";
626 }
627 else if (applyTime == "OnReset")
628 {
629 applyTimeNewVal =
630 "xyz.openbmc_project.Software.ApplyTime."
631 "RequestedApplyTimes.OnReset";
632 }
633 else
634 {
635 BMCWEB_LOG_INFO
636 << "ApplyTime value is not in the list of "
637 "acceptable values";
638 messages::propertyValueNotInList(
639 asyncResp->res, *applyTime, "ApplyTime");
640 return;
641 }
642
643 // Set the requested image apply time value
644 crow::connections::systemBus->async_method_call(
645 [asyncResp](
646 const boost::system::error_code ec) {
647 if (ec)
648 {
649 BMCWEB_LOG_ERROR
650 << "D-Bus responses error: " << ec;
651 messages::internalError(asyncResp->res);
652 return;
653 }
654 messages::success(asyncResp->res);
655 },
656 "xyz.openbmc_project.Settings",
657 "/xyz/openbmc_project/software/apply_time",
658 "org.freedesktop.DBus.Properties", "Set",
659 "xyz.openbmc_project.Software.ApplyTime",
660 "RequestedApplyTime",
661 std::variant<std::string>{applyTimeNewVal});
662 }
663 }
664 }
665 });
666 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700667 .privileges(redfish::privileges::postUpdateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700668 .methods(boost::beast::http::verb::post)(
669 [](const crow::Request& req,
670 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
671 BMCWEB_LOG_DEBUG << "doPost...";
672
673 // Setup callback for when new software detected
674 monitorForSoftwareAvailable(asyncResp, req,
675 "/redfish/v1/UpdateService");
676
677 std::string filepath("/tmp/images/" +
678 boost::uuids::to_string(
679 boost::uuids::random_generator()()));
680 BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
681 std::ofstream out(filepath, std::ofstream::out |
682 std::ofstream::binary |
683 std::ofstream::trunc);
684 out << req.body;
685 out.close();
686 BMCWEB_LOG_DEBUG << "file upload complete!!";
687 });
688}
689
690inline void requestRoutesSoftwareInventoryCollection(App& app)
691{
692 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
Ed Tanoused398212021-06-09 17:05:54 -0700693 .privileges(redfish::privileges::getSoftwareInventoryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700694 .methods(boost::beast::http::verb::get)(
695 [](const crow::Request&,
696 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
697 asyncResp->res.jsonValue["@odata.type"] =
698 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
699 asyncResp->res.jsonValue["@odata.id"] =
700 "/redfish/v1/UpdateService/FirmwareInventory";
701 asyncResp->res.jsonValue["Name"] =
702 "Software Inventory Collection";
703
704 crow::connections::systemBus->async_method_call(
705 [asyncResp](
706 const boost::system::error_code ec,
707 const std::vector<std::pair<
708 std::string,
709 std::vector<std::pair<std::string,
710 std::vector<std::string>>>>>&
711 subtree) {
712 if (ec)
713 {
714 messages::internalError(asyncResp->res);
715 return;
716 }
717 asyncResp->res.jsonValue["Members"] =
718 nlohmann::json::array();
719 asyncResp->res.jsonValue["Members@odata.count"] = 0;
720
721 for (auto& obj : subtree)
722 {
723 sdbusplus::message::object_path path(obj.first);
724 std::string swId = path.filename();
725 if (swId.empty())
726 {
727 messages::internalError(asyncResp->res);
728 BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
729 return;
730 }
731
732 nlohmann::json& members =
733 asyncResp->res.jsonValue["Members"];
734 members.push_back(
735 {{"@odata.id", "/redfish/v1/UpdateService/"
736 "FirmwareInventory/" +
737 swId}});
738 asyncResp->res.jsonValue["Members@odata.count"] =
739 members.size();
740 }
741 },
742 // Note that only firmware levels associated with a device
743 // are stored under /xyz/openbmc_project/software therefore
744 // to ensure only real FirmwareInventory items are returned,
745 // this full object path must be used here as input to
746 // mapper
747 "xyz.openbmc_project.ObjectMapper",
748 "/xyz/openbmc_project/object_mapper",
749 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
750 "/xyz/openbmc_project/software", static_cast<int32_t>(0),
751 std::array<const char*, 1>{
752 "xyz.openbmc_project.Software.Version"});
753 });
754}
755/* Fill related item links (i.e. bmc, bios) in for inventory */
756inline static void
757 getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
758 const std::string& purpose)
759{
760 if (purpose == fw_util::bmcPurpose)
761 {
762 nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
763 relatedItem.push_back({{"@odata.id", "/redfish/v1/Managers/bmc"}});
764 aResp->res.jsonValue["RelatedItem@odata.count"] = relatedItem.size();
765 }
766 else if (purpose == fw_util::biosPurpose)
767 {
768 nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
769 relatedItem.push_back(
770 {{"@odata.id", "/redfish/v1/Systems/system/Bios"}});
771 aResp->res.jsonValue["Members@odata.count"] = relatedItem.size();
772 }
773 else
774 {
775 BMCWEB_LOG_ERROR << "Unknown software purpose " << purpose;
776 }
777}
778
779inline void requestRoutesSoftwareInventory(App& app)
780{
781 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700782 .privileges(redfish::privileges::getSoftwareInventory)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700783 .methods(
784 boost::beast::http::verb::get)([](const crow::Request&,
785 const std::shared_ptr<
786 bmcweb::AsyncResp>& asyncResp,
787 const std::string& param) {
788 std::shared_ptr<std::string> swId =
789 std::make_shared<std::string>(param);
790
791 asyncResp->res.jsonValue["@odata.id"] =
792 "/redfish/v1/UpdateService/FirmwareInventory/" + *swId;
793
794 crow::connections::systemBus->async_method_call(
795 [asyncResp, swId](
796 const boost::system::error_code ec,
797 const std::vector<
798 std::pair<std::string,
799 std::vector<std::pair<
800 std::string, std::vector<std::string>>>>>&
801 subtree) {
802 BMCWEB_LOG_DEBUG << "doGet callback...";
803 if (ec)
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700804 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700805 messages::internalError(asyncResp->res);
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700806 return;
807 }
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700808
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700809 // Ensure we find our input swId, otherwise return an error
810 bool found = false;
811 for (const std::pair<
812 std::string,
813 std::vector<std::pair<
814 std::string, std::vector<std::string>>>>& obj :
815 subtree)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700816 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700817 if (boost::ends_with(obj.first, *swId) != true)
818 {
819 continue;
820 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700821
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700822 if (obj.second.size() < 1)
823 {
824 continue;
825 }
826
827 found = true;
828 fw_util::getFwStatus(asyncResp, swId,
829 obj.second[0].first);
830
831 crow::connections::systemBus->async_method_call(
832 [asyncResp, swId](
833 const boost::system::error_code errorCode,
834 const boost::container::flat_map<
835 std::string, VariantType>& propertiesList) {
836 if (errorCode)
837 {
838 messages::internalError(asyncResp->res);
839 return;
840 }
841 boost::container::flat_map<
842 std::string, VariantType>::const_iterator
843 it = propertiesList.find("Purpose");
844 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 {
856 BMCWEB_LOG_DEBUG << "wrong types for "
857 "property\"Purpose\"!";
858 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