| #pragma once |
| |
| #include "app.hpp" |
| #include "dbus_singleton.hpp" |
| #include "dbus_utility.hpp" |
| #include "ossl_random.hpp" |
| |
| #include <sdbusplus/bus/match.hpp> |
| |
| #include <cstdio> |
| #include <fstream> |
| #include <memory> |
| #include <ranges> |
| |
| namespace crow |
| { |
| namespace image_upload |
| { |
| |
| // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) |
| static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher; |
| |
| inline void |
| uploadImageHandler(const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) |
| { |
| // Only allow one FW update at a time |
| if (fwUpdateMatcher != nullptr) |
| { |
| asyncResp->res.addHeader("Retry-After", "30"); |
| asyncResp->res.result(boost::beast::http::status::service_unavailable); |
| return; |
| } |
| if (req.ioService == nullptr) |
| { |
| asyncResp->res.result( |
| boost::beast::http::status::internal_server_error); |
| return; |
| } |
| // Make this const static so it survives outside this method |
| static boost::asio::steady_timer timeout(*req.ioService, |
| std::chrono::seconds(5)); |
| |
| timeout.expires_after(std::chrono::seconds(15)); |
| |
| auto timeoutHandler = [asyncResp](const boost::system::error_code& ec) { |
| fwUpdateMatcher = nullptr; |
| if (ec == boost::asio::error::operation_aborted) |
| { |
| // expected, we were canceled before the timer completed. |
| return; |
| } |
| BMCWEB_LOG_ERROR("Timed out waiting for Version interface"); |
| |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR("Async_wait failed {}", ec); |
| return; |
| } |
| |
| asyncResp->res.result(boost::beast::http::status::bad_request); |
| asyncResp->res.jsonValue["data"]["description"] = |
| "Version already exists or failed to be extracted"; |
| asyncResp->res.jsonValue["message"] = "400 Bad Request"; |
| asyncResp->res.jsonValue["status"] = "error"; |
| }; |
| |
| std::function<void(sdbusplus::message_t&)> callback = |
| [asyncResp](sdbusplus::message_t& m) { |
| BMCWEB_LOG_DEBUG("Match fired"); |
| |
| sdbusplus::message::object_path path; |
| dbus::utility::DBusInterfacesMap interfaces; |
| m.read(path, interfaces); |
| |
| if (std::ranges::find_if(interfaces, [](const auto& i) { |
| return i.first == "xyz.openbmc_project.Software.Version"; |
| }) != interfaces.end()) |
| { |
| timeout.cancel(); |
| std::string leaf = path.filename(); |
| if (leaf.empty()) |
| { |
| leaf = path.str; |
| } |
| |
| asyncResp->res.jsonValue["data"] = leaf; |
| asyncResp->res.jsonValue["message"] = "200 OK"; |
| asyncResp->res.jsonValue["status"] = "ok"; |
| BMCWEB_LOG_DEBUG("ending response"); |
| fwUpdateMatcher = nullptr; |
| } |
| }; |
| fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>( |
| *crow::connections::systemBus, |
| "interface='org.freedesktop.DBus.ObjectManager',type='signal'," |
| "member='InterfacesAdded',path='/xyz/openbmc_project/software'", |
| callback); |
| |
| std::string filepath("/tmp/images/" + bmcweb::getRandomUUID()); |
| BMCWEB_LOG_DEBUG("Writing file to {}", filepath); |
| std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary | |
| std::ofstream::trunc); |
| out << req.body(); |
| out.close(); |
| timeout.async_wait(timeoutHandler); |
| } |
| |
| inline void requestRoutes(App& app) |
| { |
| BMCWEB_ROUTE(app, "/upload/image/<str>") |
| .privileges({{"ConfigureComponents", "ConfigureManager"}}) |
| .methods(boost::beast::http::verb::post, boost::beast::http::verb::put)( |
| [](const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string&) { uploadImageHandler(req, asyncResp); }); |
| |
| BMCWEB_ROUTE(app, "/upload/image") |
| .privileges({{"ConfigureComponents", "ConfigureManager"}}) |
| .methods(boost::beast::http::verb::post, boost::beast::http::verb::put)( |
| [](const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { |
| uploadImageHandler(req, asyncResp); |
| }); |
| } |
| } // namespace image_upload |
| } // namespace crow |