blob: 4644300ccdc36b393ec4c05933b6ecca001fb10e [file] [log] [blame]
Ed Tanous40e9b922024-09-10 13:50:16 -07001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright OpenBMC Authors
Ed Tanousc3ee5222018-05-01 12:58:27 -07003#pragma once
4
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08005#include "app.hpp"
6#include "dbus_singleton.hpp"
7#include "dbus_utility.hpp"
Ed Tanous2c6ffdb2023-06-28 11:28:38 -07008#include "ossl_random.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08009
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080010#include <sdbusplus/bus/match.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050011
12#include <cstdio>
Ed Tanous1abe55e2018-09-05 08:30:59 -070013#include <fstream>
14#include <memory>
Ed Tanous3544d2a2023-08-06 18:12:20 -070015#include <ranges>
Ed Tanousc3ee5222018-05-01 12:58:27 -070016
Ed Tanous1abe55e2018-09-05 08:30:59 -070017namespace crow
18{
19namespace image_upload
20{
Ed Tanousc3ee5222018-05-01 12:58:27 -070021
Ed Tanouscf9e4172022-12-21 09:30:16 -080022// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050023static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
Ed Tanousc3ee5222018-05-01 12:58:27 -070024
zhanghch058d1b46d2021-04-01 11:18:24 +080025inline void
26 uploadImageHandler(const crow::Request& req,
27 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -070028{
29 // Only allow one FW update at a time
30 if (fwUpdateMatcher != nullptr)
31 {
zhanghch058d1b46d2021-04-01 11:18:24 +080032 asyncResp->res.addHeader("Retry-After", "30");
33 asyncResp->res.result(boost::beast::http::status::service_unavailable);
Ed Tanous1abe55e2018-09-05 08:30:59 -070034 return;
35 }
Ed Tanous8e8245d2024-04-11 22:21:38 -070036 if (req.ioService == nullptr)
37 {
38 asyncResp->res.result(
39 boost::beast::http::status::internal_server_error);
40 return;
41 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070042 // Make this const static so it survives outside this method
Ed Tanous271584a2019-07-09 16:24:22 -070043 static boost::asio::steady_timer timeout(*req.ioService,
44 std::chrono::seconds(5));
Ed Tanous1abe55e2018-09-05 08:30:59 -070045
Ed Tanous271584a2019-07-09 16:24:22 -070046 timeout.expires_after(std::chrono::seconds(15));
Ed Tanous1abe55e2018-09-05 08:30:59 -070047
zhanghch058d1b46d2021-04-01 11:18:24 +080048 auto timeoutHandler = [asyncResp](const boost::system::error_code& ec) {
Ed Tanousc3ee5222018-05-01 12:58:27 -070049 fwUpdateMatcher = nullptr;
Ed Tanous23e64202020-09-15 19:21:30 -070050 if (ec == boost::asio::error::operation_aborted)
Ed Tanous1abe55e2018-09-05 08:30:59 -070051 {
52 // expected, we were canceled before the timer completed.
53 return;
54 }
Ed Tanous62598e32023-07-17 17:06:25 -070055 BMCWEB_LOG_ERROR("Timed out waiting for Version interface");
Ed Tanousc3ee5222018-05-01 12:58:27 -070056
Ed Tanous1abe55e2018-09-05 08:30:59 -070057 if (ec)
58 {
Ed Tanous62598e32023-07-17 17:06:25 -070059 BMCWEB_LOG_ERROR("Async_wait failed {}", ec);
Ed Tanous1abe55e2018-09-05 08:30:59 -070060 return;
61 }
62
zhanghch058d1b46d2021-04-01 11:18:24 +080063 asyncResp->res.result(boost::beast::http::status::bad_request);
Ed Tanous14766872022-03-15 10:44:42 -070064 asyncResp->res.jsonValue["data"]["description"] =
65 "Version already exists or failed to be extracted";
66 asyncResp->res.jsonValue["message"] = "400 Bad Request";
67 asyncResp->res.jsonValue["status"] = "error";
Lei YU9f898f82019-03-08 16:52:10 +080068 };
Ed Tanous1abe55e2018-09-05 08:30:59 -070069
Patrick Williams59d494e2022-07-22 19:26:55 -050070 std::function<void(sdbusplus::message_t&)> callback =
71 [asyncResp](sdbusplus::message_t& m) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -040072 BMCWEB_LOG_DEBUG("Match fired");
Ed Tanous1abe55e2018-09-05 08:30:59 -070073
Patrick Williamsbd79bce2024-08-16 15:22:20 -040074 sdbusplus::message::object_path path;
75 dbus::utility::DBusInterfacesMap interfaces;
76 m.read(path, interfaces);
Matt Spinlerc9008502019-01-21 12:21:25 -060077
Patrick Williamsbd79bce2024-08-16 15:22:20 -040078 if (std::ranges::find_if(interfaces, [](const auto& i) {
79 return i.first == "xyz.openbmc_project.Software.Version";
80 }) != interfaces.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -070081 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -040082 timeout.cancel();
83 std::string leaf = path.filename();
84 if (leaf.empty())
85 {
86 leaf = path.str;
87 }
Ed Tanous002d39b2022-05-31 08:59:27 -070088
Patrick Williamsbd79bce2024-08-16 15:22:20 -040089 asyncResp->res.jsonValue["data"] = leaf;
90 asyncResp->res.jsonValue["message"] = "200 OK";
91 asyncResp->res.jsonValue["status"] = "ok";
92 BMCWEB_LOG_DEBUG("ending response");
93 fwUpdateMatcher = nullptr;
94 }
95 };
Patrick Williams59d494e2022-07-22 19:26:55 -050096 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
Ed Tanous1abe55e2018-09-05 08:30:59 -070097 *crow::connections::systemBus,
98 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
Matt Spinlerc9008502019-01-21 12:21:25 -060099 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700100 callback);
101
Ed Tanous2c6ffdb2023-06-28 11:28:38 -0700102 std::string filepath("/tmp/images/" + bmcweb::getRandomUUID());
Ed Tanous62598e32023-07-17 17:06:25 -0700103 BMCWEB_LOG_DEBUG("Writing file to {}", filepath);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700104 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
105 std::ofstream::trunc);
Ed Tanous33c6b582023-02-14 15:05:48 -0800106 out << req.body();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700107 out.close();
Lei YU9f898f82019-03-08 16:52:10 +0800108 timeout.async_wait(timeoutHandler);
Ed Tanousc3ee5222018-05-01 12:58:27 -0700109}
110
Ed Tanous23a21a12020-07-25 04:45:05 +0000111inline void requestRoutes(App& app)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700112{
113 BMCWEB_ROUTE(app, "/upload/image/<str>")
Ed Tanous432a8902021-06-14 15:28:56 -0700114 .privileges({{"ConfigureComponents", "ConfigureManager"}})
Ed Tanousb41187f2019-10-24 16:30:02 -0700115 .methods(boost::beast::http::verb::post, boost::beast::http::verb::put)(
zhanghch058d1b46d2021-04-01 11:18:24 +0800116 [](const crow::Request& req,
117 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
118 const std::string&) { uploadImageHandler(req, asyncResp); });
Ed Tanousc3ee5222018-05-01 12:58:27 -0700119
Ed Tanous1abe55e2018-09-05 08:30:59 -0700120 BMCWEB_ROUTE(app, "/upload/image")
Ed Tanous432a8902021-06-14 15:28:56 -0700121 .privileges({{"ConfigureComponents", "ConfigureManager"}})
Ed Tanousb41187f2019-10-24 16:30:02 -0700122 .methods(boost::beast::http::verb::post, boost::beast::http::verb::put)(
zhanghch058d1b46d2021-04-01 11:18:24 +0800123 [](const crow::Request& req,
124 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400125 uploadImageHandler(req, asyncResp);
126 });
Ed Tanousc3ee5222018-05-01 12:58:27 -0700127}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700128} // namespace image_upload
129} // namespace crow