1 #pragma once 2 3 #include <crow/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 <cstdio> 9 #include <dbus_singleton.hpp> 10 #include <fstream> 11 #include <memory> 12 13 namespace crow 14 { 15 namespace image_upload 16 { 17 18 std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher; 19 20 inline void uploadImageHandler(const crow::Request& req, crow::Response& res, 21 const std::string& filename) 22 { 23 // Only allow one FW update at a time 24 if (fwUpdateMatcher != nullptr) 25 { 26 res.addHeader("Retry-After", "30"); 27 res.result(boost::beast::http::status::service_unavailable); 28 res.end(); 29 return; 30 } 31 // Make this const static so it survives outside this method 32 static boost::asio::deadline_timer timeout(*req.ioService, 33 boost::posix_time::seconds(5)); 34 35 timeout.expires_from_now(boost::posix_time::seconds(10)); 36 37 timeout.async_wait([&res](const boost::system::error_code& ec) { 38 fwUpdateMatcher = nullptr; 39 if (ec == 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 res.result(boost::beast::http::status::bad_request); 53 res.jsonValue = { 54 {"data", 55 {{"description", 56 "Version already exists or failed to be extracted"}}}, 57 {"message", "400 Bad Request"}, 58 {"status", "error"}}; 59 res.end(); 60 }); 61 62 std::function<void(sdbusplus::message::message&)> callback = 63 [&res](sdbusplus::message::message& m) { 64 BMCWEB_LOG_DEBUG << "Match fired"; 65 66 sdbusplus::message::object_path path; 67 std::vector<std::pair< 68 std::string, 69 std::vector<std::pair< 70 std::string, sdbusplus::message::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 boost::system::error_code ec; 81 timeout.cancel(ec); 82 if (ec) 83 { 84 BMCWEB_LOG_ERROR << "error canceling timer " << ec; 85 } 86 87 std::size_t index = path.str.rfind('/'); 88 if (index != std::string::npos) 89 { 90 path.str.erase(0, index + 1); 91 } 92 res.jsonValue = {{"data", std::move(path.str)}, 93 {"message", "200 OK"}, 94 {"status", "ok"}}; 95 BMCWEB_LOG_DEBUG << "ending response"; 96 res.end(); 97 fwUpdateMatcher = nullptr; 98 } 99 }; 100 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>( 101 *crow::connections::systemBus, 102 "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 103 "member='InterfacesAdded',path='/xyz/openbmc_project/software'", 104 callback); 105 106 std::string filepath( 107 "/tmp/images/" + 108 boost::uuids::to_string(boost::uuids::random_generator()())); 109 BMCWEB_LOG_DEBUG << "Writing file to " << filepath; 110 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary | 111 std::ofstream::trunc); 112 out << req.body; 113 out.close(); 114 } 115 116 template <typename... Middlewares> void requestRoutes(Crow<Middlewares...>& app) 117 { 118 BMCWEB_ROUTE(app, "/upload/image/<str>") 119 .methods("POST"_method, 120 "PUT"_method)([](const crow::Request& req, crow::Response& res, 121 const std::string& filename) { 122 uploadImageHandler(req, res, filename); 123 }); 124 125 BMCWEB_ROUTE(app, "/upload/image") 126 .methods("POST"_method, "PUT"_method)( 127 [](const crow::Request& req, crow::Response& res) { 128 uploadImageHandler(req, res, ""); 129 }); 130 } 131 } // namespace image_upload 132 } // namespace crow 133