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 #include <dbus_utility.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_t> fwUpdateMatcher; 20 21 inline void 22 uploadImageHandler(const crow::Request& req, 23 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 24 { 25 // Only allow one FW update at a time 26 if (fwUpdateMatcher != nullptr) 27 { 28 asyncResp->res.addHeader("Retry-After", "30"); 29 asyncResp->res.result(boost::beast::http::status::service_unavailable); 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 = [asyncResp](const boost::system::error_code& ec) { 39 fwUpdateMatcher = nullptr; 40 if (ec == boost::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 asyncResp->res.result(boost::beast::http::status::bad_request); 54 asyncResp->res.jsonValue["data"]["description"] = 55 "Version already exists or failed to be extracted"; 56 asyncResp->res.jsonValue["message"] = "400 Bad Request"; 57 asyncResp->res.jsonValue["status"] = "error"; 58 }; 59 60 std::function<void(sdbusplus::message_t&)> callback = 61 [asyncResp](sdbusplus::message_t& m) { 62 BMCWEB_LOG_DEBUG << "Match fired"; 63 64 sdbusplus::message::object_path path; 65 dbus::utility::DBusInteracesMap interfaces; 66 m.read(path, interfaces); 67 68 if (std::find_if(interfaces.begin(), interfaces.end(), 69 [](const auto& i) { 70 return i.first == "xyz.openbmc_project.Software.Version"; 71 }) != interfaces.end()) 72 { 73 timeout.cancel(); 74 std::string leaf = path.filename(); 75 if (leaf.empty()) 76 { 77 leaf = path.str; 78 } 79 80 asyncResp->res.jsonValue["data"] = leaf; 81 asyncResp->res.jsonValue["message"] = "200 OK"; 82 asyncResp->res.jsonValue["status"] = "ok"; 83 BMCWEB_LOG_DEBUG << "ending response"; 84 fwUpdateMatcher = nullptr; 85 } 86 }; 87 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>( 88 *crow::connections::systemBus, 89 "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 90 "member='InterfacesAdded',path='/xyz/openbmc_project/software'", 91 callback); 92 93 std::string filepath( 94 "/tmp/images/" + 95 boost::uuids::to_string(boost::uuids::random_generator()())); 96 BMCWEB_LOG_DEBUG << "Writing file to " << filepath; 97 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary | 98 std::ofstream::trunc); 99 out << req.body; 100 out.close(); 101 timeout.async_wait(timeoutHandler); 102 } 103 104 inline void requestRoutes(App& app) 105 { 106 BMCWEB_ROUTE(app, "/upload/image/<str>") 107 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 108 .methods(boost::beast::http::verb::post, boost::beast::http::verb::put)( 109 [](const crow::Request& req, 110 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 111 const std::string&) { uploadImageHandler(req, asyncResp); }); 112 113 BMCWEB_ROUTE(app, "/upload/image") 114 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 115 .methods(boost::beast::http::verb::post, boost::beast::http::verb::put)( 116 [](const crow::Request& req, 117 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 118 uploadImageHandler(req, asyncResp); 119 }); 120 } 121 } // namespace image_upload 122 } // namespace crow 123