#pragma once #include "app.hpp" #include "dbus_singleton.hpp" #include "dbus_utility.hpp" #include "ossl_random.hpp" #include #include #include #include #include namespace crow { namespace image_upload { // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) static std::unique_ptr fwUpdateMatcher; inline void uploadImageHandler(const crow::Request& req, const std::shared_ptr& 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 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( *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/") .privileges({{"ConfigureComponents", "ConfigureManager"}}) .methods(boost::beast::http::verb::post, boost::beast::http::verb::put)( [](const crow::Request& req, const std::shared_ptr& 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& asyncResp) { uploadImageHandler(req, asyncResp); }); } } // namespace image_upload } // namespace crow