1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3 #pragma once 4 5 #include "app.hpp" 6 #include "async_resp.hpp" 7 #include "dbus_singleton.hpp" 8 #include "dbus_utility.hpp" 9 #include "http_request.hpp" 10 #include "io_context_singleton.hpp" 11 #include "logging.hpp" 12 #include "ossl_random.hpp" 13 14 #include <boost/asio/error.hpp> 15 #include <boost/asio/steady_timer.hpp> 16 #include <boost/beast/http/status.hpp> 17 #include <boost/beast/http/verb.hpp> 18 #include <sdbusplus/bus/match.hpp> 19 #include <sdbusplus/message.hpp> 20 #include <sdbusplus/message/native_types.hpp> 21 22 #include <algorithm> 23 #include <chrono> 24 #include <fstream> 25 #include <functional> 26 #include <memory> 27 #include <ranges> 28 #include <string> 29 30 namespace crow 31 { 32 namespace image_upload 33 { 34 35 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 36 static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher; 37 38 inline void uploadImageHandler( 39 const crow::Request& req, 40 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 41 { 42 // Only allow one FW update at a time 43 if (fwUpdateMatcher != nullptr) 44 { 45 asyncResp->res.addHeader("Retry-After", "30"); 46 asyncResp->res.result(boost::beast::http::status::service_unavailable); 47 return; 48 } 49 // Make this const static so it survives outside this method 50 static boost::asio::steady_timer timeout(getIoContext(), 51 std::chrono::seconds(5)); 52 53 timeout.expires_after(std::chrono::seconds(15)); 54 55 auto timeoutHandler = [asyncResp](const boost::system::error_code& ec) { 56 fwUpdateMatcher = nullptr; 57 if (ec == boost::asio::error::operation_aborted) 58 { 59 // expected, we were canceled before the timer completed. 60 return; 61 } 62 BMCWEB_LOG_ERROR("Timed out waiting for Version interface"); 63 64 if (ec) 65 { 66 BMCWEB_LOG_ERROR("Async_wait failed {}", ec); 67 return; 68 } 69 70 asyncResp->res.result(boost::beast::http::status::bad_request); 71 asyncResp->res.jsonValue["data"]["description"] = 72 "Version already exists or failed to be extracted"; 73 asyncResp->res.jsonValue["message"] = "400 Bad Request"; 74 asyncResp->res.jsonValue["status"] = "error"; 75 }; 76 77 std::function<void(sdbusplus::message_t&)> callback = 78 [asyncResp](sdbusplus::message_t& m) { 79 BMCWEB_LOG_DEBUG("Match fired"); 80 81 sdbusplus::message::object_path path; 82 dbus::utility::DBusInterfacesMap interfaces; 83 m.read(path, interfaces); 84 85 if (std::ranges::find_if(interfaces, [](const auto& i) { 86 return i.first == "xyz.openbmc_project.Software.Version"; 87 }) != interfaces.end()) 88 { 89 timeout.cancel(); 90 std::string leaf = path.filename(); 91 if (leaf.empty()) 92 { 93 leaf = path.str; 94 } 95 96 asyncResp->res.jsonValue["data"] = leaf; 97 asyncResp->res.jsonValue["message"] = "200 OK"; 98 asyncResp->res.jsonValue["status"] = "ok"; 99 BMCWEB_LOG_DEBUG("ending response"); 100 fwUpdateMatcher = nullptr; 101 } 102 }; 103 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>( 104 *crow::connections::systemBus, 105 "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 106 "member='InterfacesAdded',path='/xyz/openbmc_project/software'", 107 callback); 108 109 std::string filepath("/tmp/images/" + bmcweb::getRandomUUID()); 110 BMCWEB_LOG_DEBUG("Writing file to {}", filepath); 111 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary | 112 std::ofstream::trunc); 113 out << req.body(); 114 out.close(); 115 timeout.async_wait(timeoutHandler); 116 } 117 118 inline void requestRoutes(App& app) 119 { 120 BMCWEB_ROUTE(app, "/upload/image/<str>") 121 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 122 .methods(boost::beast::http::verb::post, boost::beast::http::verb::put)( 123 [](const crow::Request& req, 124 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 125 const std::string&) { uploadImageHandler(req, asyncResp); }); 126 127 BMCWEB_ROUTE(app, "/upload/image") 128 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 129 .methods(boost::beast::http::verb::post, boost::beast::http::verb::put)( 130 [](const crow::Request& req, 131 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 132 uploadImageHandler(req, asyncResp); 133 }); 134 } 135 } // namespace image_upload 136 } // namespace crow 137