blob: 663d48becf772444a196711f25618b6e976e5210 [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,
68 const crow::Request& req)
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(
James Feistfe306722020-03-12 16:32:08 -070088 [objPath, asyncResp,
Ed Tanous81ce6092020-12-17 16:54:55 +000089 req](const boost::system::error_code errorCode,
James Feistfe306722020-03-12 16:32:08 -070090 const std::vector<std::pair<
Gunnar Mills1214b7e2020-06-04 10:11:30 -050091 std::string, std::vector<std::string>>>& objInfo) {
Ed Tanous81ce6092020-12-17 16:54:55 +000092 if (errorCode)
Andrew Geissler86adcd62019-04-18 10:58:05 -050093 {
Ed Tanous81ce6092020-12-17 16:54:55 +000094 BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
Andrew Geissler86adcd62019-04-18 10:58:05 -050095 BMCWEB_LOG_DEBUG << "error msg = "
Ed Tanous81ce6092020-12-17 16:54:55 +000096 << errorCode.message();
Andrew Geissler0554c982019-04-23 14:40:12 -050097 if (asyncResp)
98 {
99 messages::internalError(asyncResp->res);
100 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500101 cleanUp();
102 return;
103 }
104 // Ensure we only got one service back
105 if (objInfo.size() != 1)
106 {
107 BMCWEB_LOG_ERROR << "Invalid Object Size "
108 << objInfo.size();
Andrew Geissler0554c982019-04-23 14:40:12 -0500109 if (asyncResp)
110 {
111 messages::internalError(asyncResp->res);
112 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500113 cleanUp();
114 return;
115 }
116 // cancel timer only when
117 // xyz.openbmc_project.Software.Activation interface
118 // is added
119 fwAvailableTimer = nullptr;
120
121 activateImage(objPath.str, objInfo[0].first);
Andrew Geissler0554c982019-04-23 14:40:12 -0500122 if (asyncResp)
123 {
James Feist32898ce2020-03-10 16:16:52 -0700124 std::shared_ptr<task::TaskData> task =
125 task::TaskData::createTask(
126 [](boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500127 sdbusplus::message::message& msg,
128 const std::shared_ptr<task::TaskData>&
129 taskData) {
James Feist32898ce2020-03-10 16:16:52 -0700130 if (ec)
131 {
132 return task::completed;
133 }
134
135 std::string iface;
136 boost::container::flat_map<
James Feistfd9ab9e2020-05-19 13:48:07 -0700137 std::string,
138 std::variant<std::string, uint8_t>>
James Feist32898ce2020-03-10 16:16:52 -0700139 values;
James Feist32898ce2020-03-10 16:16:52 -0700140
James Feiste5d50062020-05-11 17:29:00 -0700141 std::string index =
142 std::to_string(taskData->index);
James Feistfd9ab9e2020-05-19 13:48:07 -0700143 msg.read(iface, values);
James Feiste5d50062020-05-11 17:29:00 -0700144
James Feistfd9ab9e2020-05-19 13:48:07 -0700145 if (iface == "xyz.openbmc_project.Software."
146 "Activation")
James Feist32898ce2020-03-10 16:16:52 -0700147 {
James Feistfd9ab9e2020-05-19 13:48:07 -0700148 auto findActivation =
149 values.find("Activation");
150 if (findActivation == values.end())
151 {
152 return !task::completed;
153 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500154 std::string* state =
James Feistfd9ab9e2020-05-19 13:48:07 -0700155 std::get_if<std::string>(
156 &(findActivation->second));
157
158 if (state == nullptr)
159 {
160 taskData->messages.emplace_back(
161 messages::internalError());
162 return task::completed;
163 }
164
165 if (boost::ends_with(*state,
166 "Invalid") ||
167 boost::ends_with(*state, "Failed"))
168 {
169 taskData->state = "Exception";
170 taskData->status = "Warning";
171 taskData->messages.emplace_back(
172 messages::taskAborted(index));
173 return task::completed;
174 }
175
176 if (boost::ends_with(*state, "Staged"))
177 {
178 taskData->state = "Stopping";
179 taskData->messages.emplace_back(
180 messages::taskPaused(index));
181
182 // its staged, set a long timer to
183 // allow them time to complete the
184 // update (probably cycle the
185 // system) if this expires then
186 // task will be cancelled
187 taskData->extendTimer(
188 std::chrono::hours(5));
189 return !task::completed;
190 }
191
192 if (boost::ends_with(*state, "Active"))
193 {
194 taskData->messages.emplace_back(
195 messages::taskCompletedOK(
196 index));
197 taskData->state = "Completed";
198 return task::completed;
199 }
James Feist32898ce2020-03-10 16:16:52 -0700200 }
James Feistfd9ab9e2020-05-19 13:48:07 -0700201 else if (iface ==
202 "xyz.openbmc_project.Software."
203 "ActivationProgress")
James Feist32898ce2020-03-10 16:16:52 -0700204 {
James Feistfd9ab9e2020-05-19 13:48:07 -0700205 auto findProgress =
206 values.find("Progress");
207 if (findProgress == values.end())
208 {
209 return !task::completed;
210 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500211 uint8_t* progress =
James Feistfd9ab9e2020-05-19 13:48:07 -0700212 std::get_if<uint8_t>(
213 &(findProgress->second));
James Feist32898ce2020-03-10 16:16:52 -0700214
James Feistfd9ab9e2020-05-19 13:48:07 -0700215 if (progress == nullptr)
216 {
217 taskData->messages.emplace_back(
218 messages::internalError());
219 return task::completed;
220 }
George Liu6868ff52021-01-02 11:37:41 +0800221 taskData->percentComplete =
222 static_cast<int>(*progress);
James Feist32898ce2020-03-10 16:16:52 -0700223 taskData->messages.emplace_back(
James Feistfd9ab9e2020-05-19 13:48:07 -0700224 messages::taskProgressChanged(
225 index, static_cast<size_t>(
226 *progress)));
227
228 // if we're getting status updates it's
229 // still alive, update timer
230 taskData->extendTimer(
231 std::chrono::minutes(5));
James Feist32898ce2020-03-10 16:16:52 -0700232 }
233
234 // as firmware update often results in a
235 // reboot, the task may never "complete"
236 // unless it is an error
237
238 return !task::completed;
239 },
240 "type='signal',interface='org.freedesktop.DBus."
241 "Properties',"
James Feistfd9ab9e2020-05-19 13:48:07 -0700242 "member='PropertiesChanged',path='" +
James Feist32898ce2020-03-10 16:16:52 -0700243 objPath.str + "'");
244 task->startTimer(std::chrono::minutes(5));
245 task->populateResp(asyncResp->res);
James Feistfe306722020-03-12 16:32:08 -0700246 task->payload.emplace(req);
Andrew Geissler0554c982019-04-23 14:40:12 -0500247 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500248 fwUpdateInProgress = false;
249 },
250 "xyz.openbmc_project.ObjectMapper",
251 "/xyz/openbmc_project/object_mapper",
252 "xyz.openbmc_project.ObjectMapper", "GetObject", objPath.str,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500253 std::array<const char*, 1>{
Andrew Geissler86adcd62019-04-18 10:58:05 -0500254 "xyz.openbmc_project.Software.Activation"});
255 }
256 }
257}
258
Andrew Geissler0554c982019-04-23 14:40:12 -0500259// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
260// then no asyncResp updates will occur
Ed Tanousb5a76932020-09-29 16:16:58 -0700261static void monitorForSoftwareAvailable(
zhanghch058d1b46d2021-04-01 11:18:24 +0800262 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
263 const crow::Request& req, const std::string& url,
264 int timeoutTimeSeconds = 10)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500265{
266 // Only allow one FW update at a time
267 if (fwUpdateInProgress != false)
268 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500269 if (asyncResp)
270 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500271 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
272 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500273 return;
274 }
275
Andrew Geissler0554c982019-04-23 14:40:12 -0500276 fwAvailableTimer =
Ed Tanous271584a2019-07-09 16:24:22 -0700277 std::make_unique<boost::asio::steady_timer>(*req.ioService);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500278
Ed Tanous271584a2019-07-09 16:24:22 -0700279 fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500280
281 fwAvailableTimer->async_wait(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500282 [asyncResp](const boost::system::error_code& ec) {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500283 cleanUp();
284 if (ec == boost::asio::error::operation_aborted)
285 {
286 // expected, we were canceled before the timer completed.
287 return;
288 }
289 BMCWEB_LOG_ERROR
290 << "Timed out waiting for firmware object being created";
291 BMCWEB_LOG_ERROR
292 << "FW image may has already been uploaded to server";
293 if (ec)
294 {
295 BMCWEB_LOG_ERROR << "Async_wait failed" << ec;
296 return;
297 }
Andrew Geissler0554c982019-04-23 14:40:12 -0500298 if (asyncResp)
299 {
300 redfish::messages::internalError(asyncResp->res);
301 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500302 });
303
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500304 auto callback = [asyncResp, req](sdbusplus::message::message& m) {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500305 BMCWEB_LOG_DEBUG << "Match fired";
James Feistfe306722020-03-12 16:32:08 -0700306 softwareInterfaceAdded(asyncResp, m, req);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500307 };
308
309 fwUpdateInProgress = true;
310
311 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
312 *crow::connections::systemBus,
313 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
314 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
315 callback);
James Feist4cde5d92020-06-11 10:39:55 -0700316
317 fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match::match>(
318 *crow::connections::systemBus,
319 "type='signal',member='PropertiesChanged',path_namespace='/xyz/"
320 "openbmc_project/logging/entry',"
321 "arg0='xyz.openbmc_project.Logging.Entry'",
322 [asyncResp, url](sdbusplus::message::message& m) {
323 BMCWEB_LOG_DEBUG << "Error Match fired";
324 boost::container::flat_map<std::string, std::variant<std::string>>
325 values;
326 std::string objName;
327 m.read(objName, values);
328 auto find = values.find("Message");
329 if (find == values.end())
330 {
331 return;
332 }
333 std::string* type = std::get_if<std::string>(&(find->second));
334 if (type == nullptr)
335 {
336 return; // if this was our message, timeout will cover it
337 }
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600338 if (!boost::starts_with(*type, "xyz.openbmc_project.Software"))
James Feist4cde5d92020-06-11 10:39:55 -0700339 {
340 return;
341 }
342 if (*type ==
343 "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
344 {
345 redfish::messages::invalidUpload(asyncResp->res, url,
346 "Invalid archive");
347 }
348 else if (*type == "xyz.openbmc_project.Software.Image.Error."
349 "ManifestFileFailure")
350 {
351 redfish::messages::invalidUpload(asyncResp->res, url,
352 "Invalid manifest");
353 }
354 else if (*type ==
355 "xyz.openbmc_project.Software.Image.Error.ImageFailure")
356 {
357 redfish::messages::invalidUpload(asyncResp->res, url,
358 "Invalid image format");
359 }
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600360 else if (*type == "xyz.openbmc_project.Software.Version.Error."
361 "AlreadyExists")
362 {
363
364 redfish::messages::invalidUpload(
365 asyncResp->res, url, "Image version already exists");
366
367 redfish::messages::resourceAlreadyExists(
Chicago Duan0588a3b2021-06-10 18:20:36 +0800368 asyncResp->res, "UpdateService.v1_5_0.UpdateService",
Gunnar Mills88b3dd12020-11-20 14:26:04 -0600369 "Version", "uploaded version");
370 }
James Feist4cde5d92020-06-11 10:39:55 -0700371 else if (*type ==
372 "xyz.openbmc_project.Software.Image.Error.BusyFailure")
373 {
374 redfish::messages::resourceExhaustion(asyncResp->res, url);
375 }
376 else
377 {
378 redfish::messages::internalError(asyncResp->res);
379 }
380 fwAvailableTimer = nullptr;
381 });
Andrew Geissler86adcd62019-04-18 10:58:05 -0500382}
Jennifer Lee729dae72018-04-24 15:59:34 -0700383
Andrew Geissler0554c982019-04-23 14:40:12 -0500384/**
385 * UpdateServiceActionsSimpleUpdate class supports handle POST method for
386 * SimpleUpdate action.
387 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700388inline void requestRoutesUpdateServiceActionsSimpleUpdate(App& app)
Andrew Geissler0554c982019-04-23 14:40:12 -0500389{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700390 BMCWEB_ROUTE(
391 app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
Ed Tanoused398212021-06-09 17:05:54 -0700392 .privileges(redfish::privileges::postUpdateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700393 .methods(
394 boost::beast::http::verb::
395 post)([](const crow::Request& req,
396 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
397 std::optional<std::string> transferProtocol;
398 std::string imageURI;
Andrew Geissler0554c982019-04-23 14:40:12 -0500399
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700400 BMCWEB_LOG_DEBUG << "Enter UpdateService.SimpleUpdate doPost";
Andrew Geissler0554c982019-04-23 14:40:12 -0500401
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700402 // User can pass in both TransferProtocol and ImageURI parameters or
403 // they can pass in just the ImageURI with the transfer protocol
404 // embedded within it.
405 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
406 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
Andrew Geissler0554c982019-04-23 14:40:12 -0500407
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700408 if (!json_util::readJson(req, asyncResp->res, "TransferProtocol",
409 transferProtocol, "ImageURI", imageURI))
410 {
411 BMCWEB_LOG_DEBUG
412 << "Missing TransferProtocol or ImageURI parameter";
413 return;
414 }
415 if (!transferProtocol)
416 {
417 // Must be option 2
418 // Verify ImageURI has transfer protocol in it
419 size_t separator = imageURI.find(':');
420 if ((separator == std::string::npos) ||
421 ((separator + 1) > imageURI.size()))
422 {
423 messages::actionParameterValueTypeError(
424 asyncResp->res, imageURI, "ImageURI",
425 "UpdateService.SimpleUpdate");
426 BMCWEB_LOG_ERROR << "ImageURI missing transfer protocol: "
427 << imageURI;
428 return;
429 }
430 transferProtocol = imageURI.substr(0, separator);
431 // Ensure protocol is upper case for a common comparison path
432 // below
433 boost::to_upper(*transferProtocol);
434 BMCWEB_LOG_DEBUG << "Encoded transfer protocol "
435 << *transferProtocol;
Andrew Geissler0554c982019-04-23 14:40:12 -0500436
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700437 // Adjust imageURI to not have the protocol on it for parsing
438 // below
439 // ex. tftp://1.1.1.1/myfile.bin -> 1.1.1.1/myfile.bin
440 imageURI = imageURI.substr(separator + 3);
441 BMCWEB_LOG_DEBUG << "Adjusted imageUri " << imageURI;
442 }
443
444 // OpenBMC currently only supports TFTP
445 if (*transferProtocol != "TFTP")
446 {
447 messages::actionParameterNotSupported(
448 asyncResp->res, "TransferProtocol",
449 "UpdateService.SimpleUpdate");
450 BMCWEB_LOG_ERROR << "Request incorrect protocol parameter: "
451 << *transferProtocol;
452 return;
453 }
454
455 // Format should be <IP or Hostname>/<file> for imageURI
456 size_t separator = imageURI.find('/');
Andrew Geissler0554c982019-04-23 14:40:12 -0500457 if ((separator == std::string::npos) ||
458 ((separator + 1) > imageURI.size()))
459 {
460 messages::actionParameterValueTypeError(
461 asyncResp->res, imageURI, "ImageURI",
462 "UpdateService.SimpleUpdate");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700463 BMCWEB_LOG_ERROR << "Invalid ImageURI: " << imageURI;
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530464 return;
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530465 }
466
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700467 std::string tftpServer = imageURI.substr(0, separator);
468 std::string fwFile = imageURI.substr(separator + 1);
469 BMCWEB_LOG_DEBUG << "Server: " << tftpServer + " File: " << fwFile;
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530470
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700471 // Setup callback for when new software detected
472 // Give TFTP 10 minutes to complete
473 monitorForSoftwareAvailable(
474 asyncResp, req,
475 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
476 600);
477
478 // TFTP can take up to 10 minutes depending on image size and
479 // connection speed. Return to caller as soon as the TFTP operation
480 // has been started. The callback above will ensure the activate
481 // is started once the download has completed
482 redfish::messages::success(asyncResp->res);
483
484 // Call TFTP service
485 crow::connections::systemBus->async_method_call(
486 [](const boost::system::error_code ec) {
487 if (ec)
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530488 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700489 // messages::internalError(asyncResp->res);
490 cleanUp();
491 BMCWEB_LOG_DEBUG << "error_code = " << ec;
492 BMCWEB_LOG_DEBUG << "error msg = " << ec.message();
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530493 }
494 else
495 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700496 BMCWEB_LOG_DEBUG << "Call to DownloaViaTFTP Success";
497 }
498 },
499 "xyz.openbmc_project.Software.Download",
500 "/xyz/openbmc_project/software",
501 "xyz.openbmc_project.Common.TFTP", "DownloadViaTFTP", fwFile,
502 tftpServer);
503
504 BMCWEB_LOG_DEBUG << "Exit UpdateService.SimpleUpdate doPost";
505 });
506}
507
508inline void requestRoutesUpdateService(App& app)
509{
510 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700511 .privileges(redfish::privileges::getUpdateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700512 .methods(
513 boost::beast::http::verb::
514 get)([](const crow::Request&,
515 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
516 asyncResp->res.jsonValue["@odata.type"] =
Chicago Duan0588a3b2021-06-10 18:20:36 +0800517 "#UpdateService.v1_5_0.UpdateService";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700518 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
519 asyncResp->res.jsonValue["Id"] = "UpdateService";
520 asyncResp->res.jsonValue["Description"] =
521 "Service for Software Update";
522 asyncResp->res.jsonValue["Name"] = "Update Service";
523 asyncResp->res.jsonValue["HttpPushUri"] =
524 "/redfish/v1/UpdateService";
525 // UpdateService cannot be disabled
526 asyncResp->res.jsonValue["ServiceEnabled"] = true;
527 asyncResp->res.jsonValue["FirmwareInventory"] = {
528 {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}};
Tejas Patild61e5192021-06-04 15:49:35 +0530529 // Get the MaxImageSizeBytes
530 asyncResp->res.jsonValue["MaxImageSizeBytes"] =
531 bmcwebHttpReqBodyLimitMb * 1024 * 1024;
532
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700533#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
534 // Update Actions object.
535 nlohmann::json& updateSvcSimpleUpdate =
536 asyncResp->res
537 .jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
538 updateSvcSimpleUpdate["target"] =
539 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
540 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] =
541 {"TFTP"};
542#endif
543 // Get the current ApplyTime value
544 crow::connections::systemBus->async_method_call(
545 [asyncResp](const boost::system::error_code ec,
546 const std::variant<std::string>& applyTime) {
547 if (ec)
548 {
549 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
550 messages::internalError(asyncResp->res);
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530551 return;
552 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530553
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700554 const std::string* s = std::get_if<std::string>(&applyTime);
555 if (s == nullptr)
556 {
557 return;
558 }
559 // Store the ApplyTime Value
560 if (*s == "xyz.openbmc_project.Software.ApplyTime."
561 "RequestedApplyTimes.Immediate")
562 {
563 asyncResp->res
564 .jsonValue["HttpPushUriOptions"]
565 ["HttpPushUriApplyTime"]["ApplyTime"] =
566 "Immediate";
567 }
568 else if (*s == "xyz.openbmc_project.Software.ApplyTime."
569 "RequestedApplyTimes.OnReset")
570 {
571 asyncResp->res
572 .jsonValue["HttpPushUriOptions"]
573 ["HttpPushUriApplyTime"]["ApplyTime"] =
574 "OnReset";
575 }
576 },
577 "xyz.openbmc_project.Settings",
578 "/xyz/openbmc_project/software/apply_time",
579 "org.freedesktop.DBus.Properties", "Get",
580 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime");
581 });
582 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700583 .privileges(redfish::privileges::patchUpdateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700584 .methods(boost::beast::http::verb::patch)(
585 [](const crow::Request& req,
586 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
587 BMCWEB_LOG_DEBUG << "doPatch...";
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530588
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700589 std::optional<nlohmann::json> pushUriOptions;
590 if (!json_util::readJson(req, asyncResp->res,
591 "HttpPushUriOptions", pushUriOptions))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700592 {
Ed Tanousc711bf82018-07-30 16:31:33 -0700593 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700594 }
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700595
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700596 if (pushUriOptions)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700597 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700598 std::optional<nlohmann::json> pushUriApplyTime;
599 if (!json_util::readJson(*pushUriOptions, asyncResp->res,
600 "HttpPushUriApplyTime",
601 pushUriApplyTime))
602 {
603 return;
604 }
605
606 if (pushUriApplyTime)
607 {
608 std::optional<std::string> applyTime;
609 if (!json_util::readJson(*pushUriApplyTime,
610 asyncResp->res, "ApplyTime",
611 applyTime))
612 {
613 return;
614 }
615
616 if (applyTime)
617 {
618 std::string applyTimeNewVal;
619 if (applyTime == "Immediate")
620 {
621 applyTimeNewVal =
622 "xyz.openbmc_project.Software.ApplyTime."
623 "RequestedApplyTimes.Immediate";
624 }
625 else if (applyTime == "OnReset")
626 {
627 applyTimeNewVal =
628 "xyz.openbmc_project.Software.ApplyTime."
629 "RequestedApplyTimes.OnReset";
630 }
631 else
632 {
633 BMCWEB_LOG_INFO
634 << "ApplyTime value is not in the list of "
635 "acceptable values";
636 messages::propertyValueNotInList(
637 asyncResp->res, *applyTime, "ApplyTime");
638 return;
639 }
640
641 // Set the requested image apply time value
642 crow::connections::systemBus->async_method_call(
643 [asyncResp](
644 const boost::system::error_code ec) {
645 if (ec)
646 {
647 BMCWEB_LOG_ERROR
648 << "D-Bus responses error: " << ec;
649 messages::internalError(asyncResp->res);
650 return;
651 }
652 messages::success(asyncResp->res);
653 },
654 "xyz.openbmc_project.Settings",
655 "/xyz/openbmc_project/software/apply_time",
656 "org.freedesktop.DBus.Properties", "Set",
657 "xyz.openbmc_project.Software.ApplyTime",
658 "RequestedApplyTime",
659 std::variant<std::string>{applyTimeNewVal});
660 }
661 }
662 }
663 });
664 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
Ed Tanoused398212021-06-09 17:05:54 -0700665 .privileges(redfish::privileges::postUpdateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700666 .methods(boost::beast::http::verb::post)(
667 [](const crow::Request& req,
668 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
669 BMCWEB_LOG_DEBUG << "doPost...";
670
671 // Setup callback for when new software detected
672 monitorForSoftwareAvailable(asyncResp, req,
673 "/redfish/v1/UpdateService");
674
675 std::string filepath("/tmp/images/" +
676 boost::uuids::to_string(
677 boost::uuids::random_generator()()));
678 BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
679 std::ofstream out(filepath, std::ofstream::out |
680 std::ofstream::binary |
681 std::ofstream::trunc);
682 out << req.body;
683 out.close();
684 BMCWEB_LOG_DEBUG << "file upload complete!!";
685 });
686}
687
688inline void requestRoutesSoftwareInventoryCollection(App& app)
689{
690 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
Ed Tanoused398212021-06-09 17:05:54 -0700691 .privileges(redfish::privileges::getSoftwareInventoryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700692 .methods(boost::beast::http::verb::get)(
693 [](const crow::Request&,
694 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
695 asyncResp->res.jsonValue["@odata.type"] =
696 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
697 asyncResp->res.jsonValue["@odata.id"] =
698 "/redfish/v1/UpdateService/FirmwareInventory";
699 asyncResp->res.jsonValue["Name"] =
700 "Software Inventory Collection";
701
702 crow::connections::systemBus->async_method_call(
703 [asyncResp](
704 const boost::system::error_code ec,
705 const std::vector<std::pair<
706 std::string,
707 std::vector<std::pair<std::string,
708 std::vector<std::string>>>>>&
709 subtree) {
710 if (ec)
711 {
712 messages::internalError(asyncResp->res);
713 return;
714 }
715 asyncResp->res.jsonValue["Members"] =
716 nlohmann::json::array();
717 asyncResp->res.jsonValue["Members@odata.count"] = 0;
718
719 for (auto& obj : subtree)
720 {
721 sdbusplus::message::object_path path(obj.first);
722 std::string swId = path.filename();
723 if (swId.empty())
724 {
725 messages::internalError(asyncResp->res);
726 BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
727 return;
728 }
729
730 nlohmann::json& members =
731 asyncResp->res.jsonValue["Members"];
732 members.push_back(
733 {{"@odata.id", "/redfish/v1/UpdateService/"
734 "FirmwareInventory/" +
735 swId}});
736 asyncResp->res.jsonValue["Members@odata.count"] =
737 members.size();
738 }
739 },
740 // Note that only firmware levels associated with a device
741 // are stored under /xyz/openbmc_project/software therefore
742 // to ensure only real FirmwareInventory items are returned,
743 // this full object path must be used here as input to
744 // mapper
745 "xyz.openbmc_project.ObjectMapper",
746 "/xyz/openbmc_project/object_mapper",
747 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
748 "/xyz/openbmc_project/software", static_cast<int32_t>(0),
749 std::array<const char*, 1>{
750 "xyz.openbmc_project.Software.Version"});
751 });
752}
753/* Fill related item links (i.e. bmc, bios) in for inventory */
754inline static void
755 getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
756 const std::string& purpose)
757{
758 if (purpose == fw_util::bmcPurpose)
759 {
760 nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
761 relatedItem.push_back({{"@odata.id", "/redfish/v1/Managers/bmc"}});
762 aResp->res.jsonValue["RelatedItem@odata.count"] = relatedItem.size();
763 }
764 else if (purpose == fw_util::biosPurpose)
765 {
766 nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
767 relatedItem.push_back(
768 {{"@odata.id", "/redfish/v1/Systems/system/Bios"}});
769 aResp->res.jsonValue["Members@odata.count"] = relatedItem.size();
770 }
771 else
772 {
773 BMCWEB_LOG_ERROR << "Unknown software purpose " << purpose;
774 }
775}
776
777inline void requestRoutesSoftwareInventory(App& app)
778{
779 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700780 .privileges(redfish::privileges::getSoftwareInventory)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700781 .methods(
782 boost::beast::http::verb::get)([](const crow::Request&,
783 const std::shared_ptr<
784 bmcweb::AsyncResp>& asyncResp,
785 const std::string& param) {
786 std::shared_ptr<std::string> swId =
787 std::make_shared<std::string>(param);
788
789 asyncResp->res.jsonValue["@odata.id"] =
790 "/redfish/v1/UpdateService/FirmwareInventory/" + *swId;
791
792 crow::connections::systemBus->async_method_call(
793 [asyncResp, swId](
794 const boost::system::error_code ec,
795 const std::vector<
796 std::pair<std::string,
797 std::vector<std::pair<
798 std::string, std::vector<std::string>>>>>&
799 subtree) {
800 BMCWEB_LOG_DEBUG << "doGet callback...";
801 if (ec)
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700802 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700803 messages::internalError(asyncResp->res);
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700804 return;
805 }
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700806
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700807 // Ensure we find our input swId, otherwise return an error
808 bool found = false;
809 for (const std::pair<
810 std::string,
811 std::vector<std::pair<
812 std::string, std::vector<std::string>>>>& obj :
813 subtree)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700814 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700815 if (boost::ends_with(obj.first, *swId) != true)
816 {
817 continue;
818 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700819
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700820 if (obj.second.size() < 1)
821 {
822 continue;
823 }
824
825 found = true;
826 fw_util::getFwStatus(asyncResp, swId,
827 obj.second[0].first);
828
829 crow::connections::systemBus->async_method_call(
830 [asyncResp, swId](
831 const boost::system::error_code errorCode,
832 const boost::container::flat_map<
833 std::string, VariantType>& propertiesList) {
834 if (errorCode)
835 {
836 messages::internalError(asyncResp->res);
837 return;
838 }
839 boost::container::flat_map<
840 std::string, VariantType>::const_iterator
841 it = propertiesList.find("Purpose");
842 if (it == propertiesList.end())
843 {
844 BMCWEB_LOG_DEBUG
845 << "Can't find property \"Purpose\"!";
846 messages::propertyMissing(asyncResp->res,
847 "Purpose");
848 return;
849 }
850 const std::string* swInvPurpose =
851 std::get_if<std::string>(&it->second);
852 if (swInvPurpose == nullptr)
853 {
854 BMCWEB_LOG_DEBUG << "wrong types for "
855 "property\"Purpose\"!";
856 messages::propertyValueTypeError(
857 asyncResp->res, "", "Purpose");
858 return;
859 }
860
861 BMCWEB_LOG_DEBUG << "swInvPurpose = "
862 << *swInvPurpose;
863 it = propertiesList.find("Version");
864 if (it == propertiesList.end())
865 {
866 BMCWEB_LOG_DEBUG
867 << "Can't find property \"Version\"!";
868 messages::propertyMissing(asyncResp->res,
869 "Version");
870 return;
871 }
872
873 BMCWEB_LOG_DEBUG << "Version found!";
874
875 const std::string* version =
876 std::get_if<std::string>(&it->second);
877
878 if (version == nullptr)
879 {
880 BMCWEB_LOG_DEBUG
881 << "Can't find property \"Version\"!";
882
883 messages::propertyValueTypeError(
884 asyncResp->res, "", "Version");
885 return;
886 }
887 asyncResp->res.jsonValue["Version"] = *version;
888 asyncResp->res.jsonValue["Id"] = *swId;
889
890 // swInvPurpose is of format:
891 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
892 // Translate this to "ABC image"
893 size_t endDesc = swInvPurpose->rfind('.');
894 if (endDesc == std::string::npos)
895 {
896 messages::internalError(asyncResp->res);
897 return;
898 }
899 endDesc++;
900 if (endDesc >= swInvPurpose->size())
901 {
902 messages::internalError(asyncResp->res);
903 return;
904 }
905
906 std::string formatDesc =
907 swInvPurpose->substr(endDesc);
908 asyncResp->res.jsonValue["Description"] =
909 formatDesc + " image";
910 getRelatedItems(asyncResp, *swInvPurpose);
911 },
912 obj.second[0].first, obj.first,
913 "org.freedesktop.DBus.Properties", "GetAll",
914 "xyz.openbmc_project.Software.Version");
915 }
916 if (!found)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700917 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700918 BMCWEB_LOG_ERROR
919 << "Input swID " + *swId + " not found!";
920 messages::resourceMissingAtURI(
921 asyncResp->res,
922 "/redfish/v1/UpdateService/FirmwareInventory/" +
923 *swId);
924 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700925 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700926 asyncResp->res.jsonValue["@odata.type"] =
927 "#SoftwareInventory.v1_1_0.SoftwareInventory";
928 asyncResp->res.jsonValue["Name"] = "Software Inventory";
929 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700930
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700931 asyncResp->res.jsonValue["Updateable"] = false;
932 fw_util::getFwUpdateableStatus(asyncResp, swId);
933 },
934 "xyz.openbmc_project.ObjectMapper",
935 "/xyz/openbmc_project/object_mapper",
936 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/",
937 static_cast<int32_t>(0),
938 std::array<const char*, 1>{
939 "xyz.openbmc_project.Software.Version"});
940 });
941}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700942
943} // namespace redfish