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