blob: fcb880e06a0ab86b7f28aa691021fe5289b07612 [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
18#include "node.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -070019
Jennifer Lee729dae72018-04-24 15:59:34 -070020#include <boost/container/flat_map.hpp>
Andrew Geissler87d84722019-02-28 14:28:39 -060021#include <utils/fw_utils.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050022
Ed Tanousabf2add2019-01-22 16:40:12 -080023#include <variant>
Jennifer Lee729dae72018-04-24 15:59:34 -070024
Ed Tanous1abe55e2018-09-05 08:30:59 -070025namespace redfish
26{
Ed Tanous27826b52018-10-29 11:40:58 -070027
Andrew Geissler0e7de462019-03-04 19:11:54 -060028// Match signals added on software path
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070029static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher;
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
35static void cleanUp()
36{
37 fwUpdateInProgress = false;
38 fwUpdateMatcher = nullptr;
39}
Gunnar Mills1214b7e2020-06-04 10:11:30 -050040static void activateImage(const std::string& objPath,
41 const std::string& service)
Andrew Geissler86adcd62019-04-18 10:58:05 -050042{
43 BMCWEB_LOG_DEBUG << "Activate image for " << objPath << " " << service;
44 crow::connections::systemBus->async_method_call(
45 [](const boost::system::error_code error_code) {
46 if (error_code)
47 {
48 BMCWEB_LOG_DEBUG << "error_code = " << error_code;
49 BMCWEB_LOG_DEBUG << "error msg = " << error_code.message();
50 }
51 },
52 service, objPath, "org.freedesktop.DBus.Properties", "Set",
53 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
54 std::variant<std::string>(
55 "xyz.openbmc_project.Software.Activation.RequestedActivations."
56 "Active"));
57}
Andrew Geissler0554c982019-04-23 14:40:12 -050058
59// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
60// then no asyncResp updates will occur
Andrew Geissler86adcd62019-04-18 10:58:05 -050061static void softwareInterfaceAdded(std::shared_ptr<AsyncResp> asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -050062 sdbusplus::message::message& m,
63 const crow::Request& req)
Andrew Geissler86adcd62019-04-18 10:58:05 -050064{
65 std::vector<std::pair<
66 std::string,
67 std::vector<std::pair<std::string, std::variant<std::string>>>>>
68 interfacesProperties;
69
70 sdbusplus::message::object_path objPath;
71
72 m.read(objPath, interfacesProperties);
73
74 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
Gunnar Mills1214b7e2020-06-04 10:11:30 -050075 for (auto& interface : interfacesProperties)
Andrew Geissler86adcd62019-04-18 10:58:05 -050076 {
77 BMCWEB_LOG_DEBUG << "interface = " << interface.first;
78
79 if (interface.first == "xyz.openbmc_project.Software.Activation")
80 {
81 // Found our interface, disable callbacks
82 fwUpdateMatcher = nullptr;
83
84 // Retrieve service and activate
85 crow::connections::systemBus->async_method_call(
James Feistfe306722020-03-12 16:32:08 -070086 [objPath, asyncResp,
87 req](const boost::system::error_code error_code,
88 const std::vector<std::pair<
Gunnar Mills1214b7e2020-06-04 10:11:30 -050089 std::string, std::vector<std::string>>>& objInfo) {
Andrew Geissler86adcd62019-04-18 10:58:05 -050090 if (error_code)
91 {
92 BMCWEB_LOG_DEBUG << "error_code = " << error_code;
93 BMCWEB_LOG_DEBUG << "error msg = "
94 << error_code.message();
Andrew Geissler0554c982019-04-23 14:40:12 -050095 if (asyncResp)
96 {
97 messages::internalError(asyncResp->res);
98 }
Andrew Geissler86adcd62019-04-18 10:58:05 -050099 cleanUp();
100 return;
101 }
102 // Ensure we only got one service back
103 if (objInfo.size() != 1)
104 {
105 BMCWEB_LOG_ERROR << "Invalid Object Size "
106 << objInfo.size();
Andrew Geissler0554c982019-04-23 14:40:12 -0500107 if (asyncResp)
108 {
109 messages::internalError(asyncResp->res);
110 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500111 cleanUp();
112 return;
113 }
114 // cancel timer only when
115 // xyz.openbmc_project.Software.Activation interface
116 // is added
117 fwAvailableTimer = nullptr;
118
119 activateImage(objPath.str, objInfo[0].first);
Andrew Geissler0554c982019-04-23 14:40:12 -0500120 if (asyncResp)
121 {
James Feist32898ce2020-03-10 16:16:52 -0700122 std::shared_ptr<task::TaskData> task =
123 task::TaskData::createTask(
124 [](boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500125 sdbusplus::message::message& msg,
126 const std::shared_ptr<task::TaskData>&
127 taskData) {
James Feist32898ce2020-03-10 16:16:52 -0700128 if (ec)
129 {
130 return task::completed;
131 }
132
133 std::string iface;
134 boost::container::flat_map<
James Feistfd9ab9e2020-05-19 13:48:07 -0700135 std::string,
136 std::variant<std::string, uint8_t>>
James Feist32898ce2020-03-10 16:16:52 -0700137 values;
James Feist32898ce2020-03-10 16:16:52 -0700138
James Feiste5d50062020-05-11 17:29:00 -0700139 std::string index =
140 std::to_string(taskData->index);
James Feistfd9ab9e2020-05-19 13:48:07 -0700141 msg.read(iface, values);
James Feiste5d50062020-05-11 17:29:00 -0700142
James Feistfd9ab9e2020-05-19 13:48:07 -0700143 if (iface == "xyz.openbmc_project.Software."
144 "Activation")
James Feist32898ce2020-03-10 16:16:52 -0700145 {
James Feistfd9ab9e2020-05-19 13:48:07 -0700146 auto findActivation =
147 values.find("Activation");
148 if (findActivation == values.end())
149 {
150 return !task::completed;
151 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500152 std::string* state =
James Feistfd9ab9e2020-05-19 13:48:07 -0700153 std::get_if<std::string>(
154 &(findActivation->second));
155
156 if (state == nullptr)
157 {
158 taskData->messages.emplace_back(
159 messages::internalError());
160 return task::completed;
161 }
162
163 if (boost::ends_with(*state,
164 "Invalid") ||
165 boost::ends_with(*state, "Failed"))
166 {
167 taskData->state = "Exception";
168 taskData->status = "Warning";
169 taskData->messages.emplace_back(
170 messages::taskAborted(index));
171 return task::completed;
172 }
173
174 if (boost::ends_with(*state, "Staged"))
175 {
176 taskData->state = "Stopping";
177 taskData->messages.emplace_back(
178 messages::taskPaused(index));
179
180 // its staged, set a long timer to
181 // allow them time to complete the
182 // update (probably cycle the
183 // system) if this expires then
184 // task will be cancelled
185 taskData->extendTimer(
186 std::chrono::hours(5));
187 return !task::completed;
188 }
189
190 if (boost::ends_with(*state, "Active"))
191 {
192 taskData->messages.emplace_back(
193 messages::taskCompletedOK(
194 index));
195 taskData->state = "Completed";
196 return task::completed;
197 }
James Feist32898ce2020-03-10 16:16:52 -0700198 }
James Feistfd9ab9e2020-05-19 13:48:07 -0700199 else if (iface ==
200 "xyz.openbmc_project.Software."
201 "ActivationProgress")
James Feist32898ce2020-03-10 16:16:52 -0700202 {
James Feistfd9ab9e2020-05-19 13:48:07 -0700203 auto findProgress =
204 values.find("Progress");
205 if (findProgress == values.end())
206 {
207 return !task::completed;
208 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500209 uint8_t* progress =
James Feistfd9ab9e2020-05-19 13:48:07 -0700210 std::get_if<uint8_t>(
211 &(findProgress->second));
James Feist32898ce2020-03-10 16:16:52 -0700212
James Feistfd9ab9e2020-05-19 13:48:07 -0700213 if (progress == nullptr)
214 {
215 taskData->messages.emplace_back(
216 messages::internalError());
217 return task::completed;
218 }
James Feist32898ce2020-03-10 16:16:52 -0700219 taskData->messages.emplace_back(
James Feistfd9ab9e2020-05-19 13:48:07 -0700220 messages::taskProgressChanged(
221 index, static_cast<size_t>(
222 *progress)));
223
224 // if we're getting status updates it's
225 // still alive, update timer
226 taskData->extendTimer(
227 std::chrono::minutes(5));
James Feist32898ce2020-03-10 16:16:52 -0700228 }
229
230 // as firmware update often results in a
231 // reboot, the task may never "complete"
232 // unless it is an error
233
234 return !task::completed;
235 },
236 "type='signal',interface='org.freedesktop.DBus."
237 "Properties',"
James Feistfd9ab9e2020-05-19 13:48:07 -0700238 "member='PropertiesChanged',path='" +
James Feist32898ce2020-03-10 16:16:52 -0700239 objPath.str + "'");
240 task->startTimer(std::chrono::minutes(5));
241 task->populateResp(asyncResp->res);
James Feistfe306722020-03-12 16:32:08 -0700242 task->payload.emplace(req);
Andrew Geissler0554c982019-04-23 14:40:12 -0500243 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500244 fwUpdateInProgress = false;
245 },
246 "xyz.openbmc_project.ObjectMapper",
247 "/xyz/openbmc_project/object_mapper",
248 "xyz.openbmc_project.ObjectMapper", "GetObject", objPath.str,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500249 std::array<const char*, 1>{
Andrew Geissler86adcd62019-04-18 10:58:05 -0500250 "xyz.openbmc_project.Software.Activation"});
251 }
252 }
253}
254
Andrew Geissler0554c982019-04-23 14:40:12 -0500255// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
256// then no asyncResp updates will occur
Andrew Geissler86adcd62019-04-18 10:58:05 -0500257static void monitorForSoftwareAvailable(std::shared_ptr<AsyncResp> asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500258 const crow::Request& req,
Andrew Geissler0554c982019-04-23 14:40:12 -0500259 int timeoutTimeSeconds = 5)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500260{
261 // Only allow one FW update at a time
262 if (fwUpdateInProgress != false)
263 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500264 if (asyncResp)
265 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500266 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
267 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500268 return;
269 }
270
Andrew Geissler0554c982019-04-23 14:40:12 -0500271 fwAvailableTimer =
Ed Tanous271584a2019-07-09 16:24:22 -0700272 std::make_unique<boost::asio::steady_timer>(*req.ioService);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500273
Ed Tanous271584a2019-07-09 16:24:22 -0700274 fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500275
276 fwAvailableTimer->async_wait(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500277 [asyncResp](const boost::system::error_code& ec) {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500278 cleanUp();
279 if (ec == boost::asio::error::operation_aborted)
280 {
281 // expected, we were canceled before the timer completed.
282 return;
283 }
284 BMCWEB_LOG_ERROR
285 << "Timed out waiting for firmware object being created";
286 BMCWEB_LOG_ERROR
287 << "FW image may has already been uploaded to server";
288 if (ec)
289 {
290 BMCWEB_LOG_ERROR << "Async_wait failed" << ec;
291 return;
292 }
Andrew Geissler0554c982019-04-23 14:40:12 -0500293 if (asyncResp)
294 {
295 redfish::messages::internalError(asyncResp->res);
296 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500297 });
298
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500299 auto callback = [asyncResp, req](sdbusplus::message::message& m) {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500300 BMCWEB_LOG_DEBUG << "Match fired";
James Feistfe306722020-03-12 16:32:08 -0700301 softwareInterfaceAdded(asyncResp, m, req);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500302 };
303
304 fwUpdateInProgress = true;
305
306 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
307 *crow::connections::systemBus,
308 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
309 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
310 callback);
311}
Jennifer Lee729dae72018-04-24 15:59:34 -0700312
Andrew Geissler0554c982019-04-23 14:40:12 -0500313/**
314 * UpdateServiceActionsSimpleUpdate class supports handle POST method for
315 * SimpleUpdate action.
316 */
317class UpdateServiceActionsSimpleUpdate : public Node
318{
319 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500320 UpdateServiceActionsSimpleUpdate(CrowApp& app) :
Andrew Geissler0554c982019-04-23 14:40:12 -0500321 Node(app,
322 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
323 {
324 entityPrivileges = {
325 {boost::beast::http::verb::get, {{"Login"}}},
326 {boost::beast::http::verb::head, {{"Login"}}},
327 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
328 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
329 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
330 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
331 }
332
333 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500334 void doPost(crow::Response& res, const crow::Request& req,
335 const std::vector<std::string>& params) override
Andrew Geissler0554c982019-04-23 14:40:12 -0500336 {
337 std::optional<std::string> transferProtocol;
338 std::string imageURI;
339 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
340
341 BMCWEB_LOG_DEBUG << "Enter UpdateService.SimpleUpdate doPost";
342
343 // User can pass in both TransferProtocol and ImageURI parameters or
344 // they can pass in just the ImageURI with the transfer protocl embedded
345 // within it.
346 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
347 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
348
349 if (!json_util::readJson(req, asyncResp->res, "TransferProtocol",
350 transferProtocol, "ImageURI", imageURI))
351 {
352 BMCWEB_LOG_DEBUG
353 << "Missing TransferProtocol or ImageURI parameter";
354 return;
355 }
356 if (!transferProtocol)
357 {
358 // Must be option 2
359 // Verify ImageURI has transfer protocol in it
360 size_t separator = imageURI.find(":");
361 if ((separator == std::string::npos) ||
362 ((separator + 1) > imageURI.size()))
363 {
364 messages::actionParameterValueTypeError(
365 asyncResp->res, imageURI, "ImageURI",
366 "UpdateService.SimpleUpdate");
367 BMCWEB_LOG_ERROR << "ImageURI missing transfer protocol: "
368 << imageURI;
369 return;
370 }
371 transferProtocol = imageURI.substr(0, separator);
372 // Ensure protocol is upper case for a common comparison path below
373 boost::to_upper(*transferProtocol);
374 BMCWEB_LOG_DEBUG << "Encoded transfer protocol "
375 << *transferProtocol;
376
377 // Adjust imageURI to not have the protocol on it for parsing
378 // below
379 // ex. tftp://1.1.1.1/myfile.bin -> 1.1.1.1/myfile.bin
380 imageURI = imageURI.substr(separator + 3);
381 BMCWEB_LOG_DEBUG << "Adjusted imageUri " << imageURI;
382 }
383
384 // OpenBMC currently only supports TFTP
385 if (*transferProtocol != "TFTP")
386 {
387 messages::actionParameterNotSupported(asyncResp->res,
388 "TransferProtocol",
389 "UpdateService.SimpleUpdate");
390 BMCWEB_LOG_ERROR << "Request incorrect protocol parameter: "
391 << *transferProtocol;
392 return;
393 }
394
395 // Format should be <IP or Hostname>/<file> for imageURI
396 size_t separator = imageURI.find("/");
397 if ((separator == std::string::npos) ||
398 ((separator + 1) > imageURI.size()))
399 {
400 messages::actionParameterValueTypeError(
401 asyncResp->res, imageURI, "ImageURI",
402 "UpdateService.SimpleUpdate");
403 BMCWEB_LOG_ERROR << "Invalid ImageURI: " << imageURI;
404 return;
405 }
406
407 std::string tftpServer = imageURI.substr(0, separator);
408 std::string fwFile = imageURI.substr(separator + 1);
409 BMCWEB_LOG_DEBUG << "Server: " << tftpServer + " File: " << fwFile;
410
411 // Setup callback for when new software detected
412 // Give TFTP 2 minutes to complete
413 monitorForSoftwareAvailable(nullptr, req, 120);
414
415 // TFTP can take up to 2 minutes depending on image size and
416 // connection speed. Return to caller as soon as the TFTP operation
417 // has been started. The callback above will ensure the activate
418 // is started once the download has completed
419 redfish::messages::success(asyncResp->res);
420
421 // Call TFTP service
422 crow::connections::systemBus->async_method_call(
423 [](const boost::system::error_code ec) {
424 if (ec)
425 {
426 // messages::internalError(asyncResp->res);
427 cleanUp();
428 BMCWEB_LOG_DEBUG << "error_code = " << ec;
429 BMCWEB_LOG_DEBUG << "error msg = " << ec.message();
430 }
431 else
432 {
433 BMCWEB_LOG_DEBUG << "Call to DownloaViaTFTP Success";
434 }
435 },
436 "xyz.openbmc_project.Software.Download",
437 "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP",
438 "DownloadViaTFTP", fwFile, tftpServer);
439
440 BMCWEB_LOG_DEBUG << "Exit UpdateService.SimpleUpdate doPost";
441 }
442};
443
Ed Tanous1abe55e2018-09-05 08:30:59 -0700444class UpdateService : public Node
445{
446 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500447 UpdateService(CrowApp& app) : Node(app, "/redfish/v1/UpdateService/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700448 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700449 entityPrivileges = {
450 {boost::beast::http::verb::get, {{"Login"}}},
451 {boost::beast::http::verb::head, {{"Login"}}},
452 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
453 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
454 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
455 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Jennifer Leeacb7cfb2018-06-07 16:08:15 -0700456 }
Jennifer Leeacb7cfb2018-06-07 16:08:15 -0700457
Ed Tanous1abe55e2018-09-05 08:30:59 -0700458 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500459 void doGet(crow::Response& res, const crow::Request& req,
460 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700461 {
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530462 std::shared_ptr<AsyncResp> aResp = std::make_shared<AsyncResp>(res);
463 res.jsonValue["@odata.type"] = "#UpdateService.v1_4_0.UpdateService";
Ed Tanous0f74e642018-11-12 15:17:05 -0800464 res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
Ed Tanous0f74e642018-11-12 15:17:05 -0800465 res.jsonValue["Id"] = "UpdateService";
466 res.jsonValue["Description"] = "Service for Software Update";
467 res.jsonValue["Name"] = "Update Service";
468 res.jsonValue["HttpPushUri"] = "/redfish/v1/UpdateService";
469 // UpdateService cannot be disabled
470 res.jsonValue["ServiceEnabled"] = true;
471 res.jsonValue["FirmwareInventory"] = {
472 {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}};
Andrew Geissler0554c982019-04-23 14:40:12 -0500473#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
474 // Update Actions object.
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500475 nlohmann::json& updateSvcSimpleUpdate =
Andrew Geissler0554c982019-04-23 14:40:12 -0500476 res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
477 updateSvcSimpleUpdate["target"] =
478 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
479 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = {
480 "TFTP"};
481#endif
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530482 // Get the current ApplyTime value
483 crow::connections::systemBus->async_method_call(
484 [aResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500485 const std::variant<std::string>& applyTime) {
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530486 if (ec)
487 {
488 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
489 messages::internalError(aResp->res);
490 return;
491 }
492
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500493 const std::string* s = std::get_if<std::string>(&applyTime);
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530494 if (s == nullptr)
495 {
496 return;
497 }
498 // Store the ApplyTime Value
499 if (*s == "xyz.openbmc_project.Software.ApplyTime."
500 "RequestedApplyTimes.Immediate")
501 {
502 aResp->res.jsonValue["HttpPushUriOptions"]
503 ["HttpPushUriApplyTime"]["ApplyTime"] =
504 "Immediate";
505 }
506 else if (*s == "xyz.openbmc_project.Software.ApplyTime."
507 "RequestedApplyTimes.OnReset")
508 {
509 aResp->res.jsonValue["HttpPushUriOptions"]
510 ["HttpPushUriApplyTime"]["ApplyTime"] =
511 "OnReset";
512 }
513 },
514 "xyz.openbmc_project.Settings",
515 "/xyz/openbmc_project/software/apply_time",
516 "org.freedesktop.DBus.Properties", "Get",
517 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700518 }
Andrew Geissler0e7de462019-03-04 19:11:54 -0600519
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500520 void doPatch(crow::Response& res, const crow::Request& req,
521 const std::vector<std::string>& params) override
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530522 {
523 BMCWEB_LOG_DEBUG << "doPatch...";
524
525 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530526
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530527 std::optional<nlohmann::json> pushUriOptions;
528 if (!json_util::readJson(req, res, "HttpPushUriOptions",
529 pushUriOptions))
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530530 {
531 return;
532 }
533
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530534 if (pushUriOptions)
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530535 {
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530536 std::optional<nlohmann::json> pushUriApplyTime;
537 if (!json_util::readJson(*pushUriOptions, res,
538 "HttpPushUriApplyTime", pushUriApplyTime))
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530539 {
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530540 return;
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530541 }
542
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530543 if (pushUriApplyTime)
544 {
545 std::optional<std::string> applyTime;
546 if (!json_util::readJson(*pushUriApplyTime, res, "ApplyTime",
547 applyTime))
548 {
549 return;
550 }
551
552 if (applyTime)
553 {
554 std::string applyTimeNewVal;
555 if (applyTime == "Immediate")
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530556 {
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530557 applyTimeNewVal =
558 "xyz.openbmc_project.Software.ApplyTime."
559 "RequestedApplyTimes.Immediate";
560 }
561 else if (applyTime == "OnReset")
562 {
563 applyTimeNewVal =
564 "xyz.openbmc_project.Software.ApplyTime."
565 "RequestedApplyTimes.OnReset";
566 }
567 else
568 {
569 BMCWEB_LOG_INFO
570 << "ApplyTime value is not in the list of "
571 "acceptable values";
572 messages::propertyValueNotInList(
573 asyncResp->res, *applyTime, "ApplyTime");
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530574 return;
575 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530576
577 // Set the requested image apply time value
578 crow::connections::systemBus->async_method_call(
579 [asyncResp](const boost::system::error_code ec) {
580 if (ec)
581 {
582 BMCWEB_LOG_ERROR << "D-Bus responses error: "
583 << ec;
584 messages::internalError(asyncResp->res);
585 return;
586 }
587 messages::success(asyncResp->res);
588 },
589 "xyz.openbmc_project.Settings",
590 "/xyz/openbmc_project/software/apply_time",
591 "org.freedesktop.DBus.Properties", "Set",
592 "xyz.openbmc_project.Software.ApplyTime",
593 "RequestedApplyTime",
594 std::variant<std::string>{applyTimeNewVal});
595 }
596 }
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530597 }
598 }
599
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500600 void doPost(crow::Response& res, const crow::Request& req,
601 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700602 {
603 BMCWEB_LOG_DEBUG << "doPost...";
Jennifer Leeacb7cfb2018-06-07 16:08:15 -0700604
Andrew Geissler0e7de462019-03-04 19:11:54 -0600605 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700606
Andrew Geissler86adcd62019-04-18 10:58:05 -0500607 // Setup callback for when new software detected
608 monitorForSoftwareAvailable(asyncResp, req);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700609
610 std::string filepath(
611 "/tmp/images/" +
612 boost::uuids::to_string(boost::uuids::random_generator()()));
613 BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
614 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
615 std::ofstream::trunc);
616 out << req.body;
617 out.close();
618 BMCWEB_LOG_DEBUG << "file upload complete!!";
619 }
Jennifer Lee729dae72018-04-24 15:59:34 -0700620};
Ed Tanousc711bf82018-07-30 16:31:33 -0700621
Ed Tanous1abe55e2018-09-05 08:30:59 -0700622class SoftwareInventoryCollection : public Node
623{
624 public:
625 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500626 SoftwareInventoryCollection(CrowApp& app) :
Ed Tanous1abe55e2018-09-05 08:30:59 -0700627 Node(app, "/redfish/v1/UpdateService/FirmwareInventory/")
628 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700629 entityPrivileges = {
630 {boost::beast::http::verb::get, {{"Login"}}},
631 {boost::beast::http::verb::head, {{"Login"}}},
632 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
633 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
634 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
635 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Jennifer Lee729dae72018-04-24 15:59:34 -0700636 }
Jennifer Lee729dae72018-04-24 15:59:34 -0700637
Ed Tanous1abe55e2018-09-05 08:30:59 -0700638 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500639 void doGet(crow::Response& res, const crow::Request& req,
640 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700641 {
642 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous0f74e642018-11-12 15:17:05 -0800643 res.jsonValue["@odata.type"] =
644 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
645 res.jsonValue["@odata.id"] =
646 "/redfish/v1/UpdateService/FirmwareInventory";
Ed Tanous0f74e642018-11-12 15:17:05 -0800647 res.jsonValue["Name"] = "Software Inventory Collection";
Ed Tanousc711bf82018-07-30 16:31:33 -0700648
Ed Tanous1abe55e2018-09-05 08:30:59 -0700649 crow::connections::systemBus->async_method_call(
650 [asyncResp](
651 const boost::system::error_code ec,
652 const std::vector<std::pair<
653 std::string, std::vector<std::pair<
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500654 std::string, std::vector<std::string>>>>>&
655 subtree) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700656 if (ec)
657 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700658 messages::internalError(asyncResp->res);
Ed Tanousc711bf82018-07-30 16:31:33 -0700659 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700660 }
661 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
662 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700663
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500664 for (auto& obj : subtree)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700665 {
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700666 // if can't parse fw id then return
Ed Tanous27826b52018-10-29 11:40:58 -0700667 std::size_t idPos;
668 if ((idPos = obj.first.rfind("/")) == std::string::npos)
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700669 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700670 messages::internalError(asyncResp->res);
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700671 BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
672 return;
673 }
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700674 std::string swId = obj.first.substr(idPos + 1);
675
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500676 nlohmann::json& members =
Andrew Geisslere0dd8052019-06-18 16:05:10 -0500677 asyncResp->res.jsonValue["Members"];
678 members.push_back(
679 {{"@odata.id", "/redfish/v1/UpdateService/"
680 "FirmwareInventory/" +
681 swId}});
682 asyncResp->res.jsonValue["Members@odata.count"] =
683 members.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700684 }
685 },
Andrew Geissler2830a9c2020-01-06 10:18:11 -0600686 // Note that only firmware levels associated with a device are
687 // stored under /xyz/openbmc_project/software therefore to ensure
688 // only real FirmwareInventory items are returned, this full object
689 // path must be used here as input to mapper
Ed Tanous1abe55e2018-09-05 08:30:59 -0700690 "xyz.openbmc_project.ObjectMapper",
691 "/xyz/openbmc_project/object_mapper",
Andrew Geissler2830a9c2020-01-06 10:18:11 -0600692 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
693 "/xyz/openbmc_project/software", static_cast<int32_t>(0),
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500694 std::array<const char*, 1>{"xyz.openbmc_project.Software.Version"});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700695 }
Jennifer Lee729dae72018-04-24 15:59:34 -0700696};
697
Ed Tanous1abe55e2018-09-05 08:30:59 -0700698class SoftwareInventory : public Node
699{
700 public:
701 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500702 SoftwareInventory(CrowApp& app) :
Ed Tanous1abe55e2018-09-05 08:30:59 -0700703 Node(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/",
704 std::string())
705 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700706 entityPrivileges = {
707 {boost::beast::http::verb::get, {{"Login"}}},
708 {boost::beast::http::verb::head, {{"Login"}}},
709 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
710 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
711 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
712 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
713 }
714
715 private:
Andrew Geissler87d84722019-02-28 14:28:39 -0600716 /* Fill related item links (i.e. bmc, bios) in for inventory */
717 static void getRelatedItems(std::shared_ptr<AsyncResp> aResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500718 const std::string& purpose)
Andrew Geissler87d84722019-02-28 14:28:39 -0600719 {
720 if (purpose == fw_util::bmcPurpose)
721 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500722 nlohmann::json& members = aResp->res.jsonValue["RelatedItem"];
Andrew Geissler87d84722019-02-28 14:28:39 -0600723 members.push_back({{"@odata.id", "/redfish/v1/Managers/bmc"}});
724 aResp->res.jsonValue["Members@odata.count"] = members.size();
725 }
726 else if (purpose == fw_util::biosPurpose)
727 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500728 nlohmann::json& members = aResp->res.jsonValue["RelatedItem"];
Gunnar Millsf723d732020-02-26 11:20:49 -0600729 members.push_back(
730 {{"@odata.id", "/redfish/v1/Systems/system/Bios"}});
731 aResp->res.jsonValue["Members@odata.count"] = members.size();
Andrew Geissler87d84722019-02-28 14:28:39 -0600732 }
733 else
734 {
735 BMCWEB_LOG_ERROR << "Unknown software purpose " << purpose;
736 }
737 }
738
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500739 void doGet(crow::Response& res, const crow::Request& req,
740 const std::vector<std::string>& params) override
Ed Tanous1abe55e2018-09-05 08:30:59 -0700741 {
742 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700743
744 if (params.size() != 1)
745 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700746 messages::internalError(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700747 res.end();
748 return;
749 }
750
Ed Tanous3ae837c2018-08-07 14:41:19 -0700751 std::shared_ptr<std::string> swId =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700752 std::make_shared<std::string>(params[0]);
753
754 res.jsonValue["@odata.id"] =
Ed Tanous3ae837c2018-08-07 14:41:19 -0700755 "/redfish/v1/UpdateService/FirmwareInventory/" + *swId;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700756
757 crow::connections::systemBus->async_method_call(
Ed Tanous3ae837c2018-08-07 14:41:19 -0700758 [asyncResp, swId](
Ed Tanous1abe55e2018-09-05 08:30:59 -0700759 const boost::system::error_code ec,
760 const std::vector<std::pair<
761 std::string, std::vector<std::pair<
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500762 std::string, std::vector<std::string>>>>>&
763 subtree) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700764 BMCWEB_LOG_DEBUG << "doGet callback...";
765 if (ec)
766 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700767 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700768 return;
769 }
770
Andrew Geissler69132282019-07-01 11:00:35 -0500771 // Ensure we find our input swId, otherwise return an error
772 bool found = false;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700773 for (const std::pair<
774 std::string,
775 std::vector<
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500776 std::pair<std::string, std::vector<std::string>>>>&
777 obj : subtree)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700778 {
Ed Tanous3ae837c2018-08-07 14:41:19 -0700779 if (boost::ends_with(obj.first, *swId) != true)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700780 {
781 continue;
782 }
783
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700784 if (obj.second.size() < 1)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700785 {
786 continue;
787 }
788
Andrew Geissler69132282019-07-01 11:00:35 -0500789 found = true;
Andrew Geisslere0dd8052019-06-18 16:05:10 -0500790 fw_util::getFwStatus(asyncResp, swId, obj.second[0].first);
791
Ed Tanous1abe55e2018-09-05 08:30:59 -0700792 crow::connections::systemBus->async_method_call(
793 [asyncResp,
Ed Tanous3ae837c2018-08-07 14:41:19 -0700794 swId](const boost::system::error_code error_code,
795 const boost::container::flat_map<
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500796 std::string, VariantType>& propertiesList) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700797 if (error_code)
798 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700799 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700800 return;
801 }
802 boost::container::flat_map<
803 std::string, VariantType>::const_iterator it =
804 propertiesList.find("Purpose");
805 if (it == propertiesList.end())
806 {
807 BMCWEB_LOG_DEBUG
808 << "Can't find property \"Purpose\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700809 messages::propertyMissing(asyncResp->res,
810 "Purpose");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700811 return;
812 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500813 const std::string* swInvPurpose =
Ed Tanousabf2add2019-01-22 16:40:12 -0800814 std::get_if<std::string>(&it->second);
Ed Tanous3ae837c2018-08-07 14:41:19 -0700815 if (swInvPurpose == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700816 {
817 BMCWEB_LOG_DEBUG
818 << "wrong types for property\"Purpose\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700819 messages::propertyValueTypeError(asyncResp->res,
820 "", "Purpose");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700821 return;
822 }
823
Ed Tanous3ae837c2018-08-07 14:41:19 -0700824 BMCWEB_LOG_DEBUG << "swInvPurpose = "
825 << *swInvPurpose;
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700826 it = propertiesList.find("Version");
827 if (it == propertiesList.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700828 {
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700829 BMCWEB_LOG_DEBUG
830 << "Can't find property \"Version\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700831 messages::propertyMissing(asyncResp->res,
832 "Version");
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700833 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700834 }
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700835
836 BMCWEB_LOG_DEBUG << "Version found!";
837
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500838 const std::string* version =
Ed Tanousabf2add2019-01-22 16:40:12 -0800839 std::get_if<std::string>(&it->second);
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700840
841 if (version == nullptr)
842 {
843 BMCWEB_LOG_DEBUG
844 << "Can't find property \"Version\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700845
846 messages::propertyValueTypeError(asyncResp->res,
847 "", "Version");
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700848 return;
849 }
850 asyncResp->res.jsonValue["Version"] = *version;
851 asyncResp->res.jsonValue["Id"] = *swId;
Andrew Geissler54daabe2019-02-13 13:54:15 -0600852
853 // swInvPurpose is of format:
854 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
James Feiste2e96772019-10-03 10:51:43 -0700855 // Translate this to "ABC image"
Andrew Geissler54daabe2019-02-13 13:54:15 -0600856 size_t endDesc = swInvPurpose->rfind(".");
857 if (endDesc == std::string::npos)
858 {
859 messages::internalError(asyncResp->res);
860 return;
861 }
862 endDesc++;
863 if (endDesc >= swInvPurpose->size())
864 {
865 messages::internalError(asyncResp->res);
866 return;
867 }
868
869 std::string formatDesc =
870 swInvPurpose->substr(endDesc);
871 asyncResp->res.jsonValue["Description"] =
James Feiste2e96772019-10-03 10:51:43 -0700872 formatDesc + " image";
Andrew Geissler87d84722019-02-28 14:28:39 -0600873 getRelatedItems(asyncResp, *swInvPurpose);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700874 },
875 obj.second[0].first, obj.first,
876 "org.freedesktop.DBus.Properties", "GetAll",
877 "xyz.openbmc_project.Software.Version");
878 }
Andrew Geissler69132282019-07-01 11:00:35 -0500879 if (!found)
880 {
881 BMCWEB_LOG_ERROR << "Input swID " + *swId + " not found!";
882 messages::resourceMissingAtURI(
883 asyncResp->res,
884 "/redfish/v1/UpdateService/FirmwareInventory/" + *swId);
885 return;
886 }
Ayushi Smriti4e68c452019-09-04 14:37:55 +0530887 asyncResp->res.jsonValue["@odata.type"] =
888 "#SoftwareInventory.v1_1_0.SoftwareInventory";
Ayushi Smriti4e68c452019-09-04 14:37:55 +0530889 asyncResp->res.jsonValue["Name"] = "Software Inventory";
Ayushi Smriti4e68c452019-09-04 14:37:55 +0530890 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
AppaRao Puli3f8a7432020-01-29 02:36:32 +0530891
892 asyncResp->res.jsonValue["Updateable"] = false;
893 fw_util::getFwUpdateableStatus(asyncResp, swId);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700894 },
895 "xyz.openbmc_project.ObjectMapper",
896 "/xyz/openbmc_project/object_mapper",
Ed Tanous271584a2019-07-09 16:24:22 -0700897 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/",
898 static_cast<int32_t>(0),
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500899 std::array<const char*, 1>{"xyz.openbmc_project.Software.Version"});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700900 }
901};
902
903} // namespace redfish