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