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