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