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 uploadImageHandler(const crow::Request& req, crow::Response& res) 21 { 22 // Only allow one FW update at a time 23 if (fwUpdateMatcher != nullptr) 24 { 25 res.addHeader("Retry-After", "30"); 26 res.result(boost::beast::http::status::service_unavailable); 27 res.end(); 28 return; 29 } 30 // Make this const static so it survives outside this method 31 static boost::asio::steady_timer timeout(*req.ioService, 32 std::chrono::seconds(5)); 33 34 timeout.expires_after(std::chrono::seconds(15)); 35 36 auto timeoutHandler = [&res](const boost::system::error_code& ec) { 37 fwUpdateMatcher = nullptr; 38 if (ec == boost::asio::error::operation_aborted) 39 { 40 // expected, we were canceled before the timer completed. 41 return; 42 } 43 BMCWEB_LOG_ERROR << "Timed out waiting for Version interface"; 44 45 if (ec) 46 { 47 BMCWEB_LOG_ERROR << "Async_wait failed " << ec; 48 return; 49 } 50 51 res.result(boost::beast::http::status::bad_request); 52 res.jsonValue = { 53 {"data", 54 {{"description", 55 "Version already exists or failed to be extracted"}}}, 56 {"message", "400 Bad Request"}, 57 {"status", "error"}}; 58 res.end(); 59 }; 60 61 std::function<void(sdbusplus::message::message&)> callback = 62 [&res](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 80 std::size_t index = path.str.rfind('/'); 81 if (index != std::string::npos) 82 { 83 path.str.erase(0, index + 1); 84 } 85 res.jsonValue = {{"data", std::move(path.str)}, 86 {"message", "200 OK"}, 87 {"status", "ok"}}; 88 BMCWEB_LOG_DEBUG << "ending response"; 89 res.end(); 90 fwUpdateMatcher = nullptr; 91 } 92 }; 93 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>( 94 *crow::connections::systemBus, 95 "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 96 "member='InterfacesAdded',path='/xyz/openbmc_project/software'", 97 callback); 98 99 std::string filepath( 100 "/tmp/images/" + 101 boost::uuids::to_string(boost::uuids::random_generator()())); 102 BMCWEB_LOG_DEBUG << "Writing file to " << filepath; 103 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary | 104 std::ofstream::trunc); 105 out << req.body; 106 out.close(); 107 timeout.async_wait(timeoutHandler); 108 } 109 110 inline void requestRoutes(App& app) 111 { 112 BMCWEB_ROUTE(app, "/upload/image/<str>") 113 .privileges({"ConfigureComponents", "ConfigureManager"}) 114 .methods(boost::beast::http::verb::post, boost::beast::http::verb::put)( 115 [](const crow::Request& req, crow::Response& res, 116 const std::string&) { uploadImageHandler(req, res); }); 117 118 BMCWEB_ROUTE(app, "/upload/image") 119 .privileges({"ConfigureComponents", "ConfigureManager"}) 120 .methods(boost::beast::http::verb::post, boost::beast::http::verb::put)( 121 [](const crow::Request& req, crow::Response& res) { 122 uploadImageHandler(req, res); 123 }); 124 } 125 } // namespace image_upload 126 } // namespace crow 127