xref: /openbmc/bmcweb/include/image_upload.hpp (revision 4859bdbafbb2b78ae414fb050ad197db2f2cb28b)
1 #pragma once
2 
3 #include <dbus_singleton.hpp>
4 #include <cstdio>
5 #include <fstream>
6 #include <memory>
7 #include <crow/app.h>
8 #include <boost/uuid/uuid.hpp>
9 #include <boost/uuid/uuid_generators.hpp>
10 #include <boost/uuid/uuid_io.hpp>
11 
12 namespace crow {
13 namespace image_upload {
14 
15 std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher;
16 
17 inline void uploadImageHandler(const crow::Request& req, crow::Response& res,
18                                const std::string& filename) {
19   // Only allow one FW update at a time
20   if (fwUpdateMatcher != nullptr) {
21     res.addHeader("Retry-After", "30");
22     res.result(boost::beast::http::status::service_unavailable);
23     res.end();
24     return;
25   }
26   // Make this const static so it survives outside this method
27   static boost::asio::deadline_timer timeout(*req.ioService,
28                                              boost::posix_time::seconds(5));
29 
30   timeout.expires_from_now(boost::posix_time::seconds(5));
31 
32   timeout.async_wait([&res](const boost::system::error_code& ec) {
33     fwUpdateMatcher = nullptr;
34     if (ec == asio::error::operation_aborted) {
35       // expected, we were canceled before the timer completed.
36       return;
37     }
38     BMCWEB_LOG_ERROR << "Timed out waiting for log event";
39 
40     if (ec) {
41       BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
42       return;
43     }
44 
45     res.result(boost::beast::http::status::internal_server_error);
46     res.end();
47   });
48 
49   std::function<void(sdbusplus::message::message&)> callback =
50       [&res](sdbusplus::message::message& m) {
51         BMCWEB_LOG_DEBUG << "Match fired";
52         boost::system::error_code ec;
53         timeout.cancel(ec);
54         if (ec) {
55           BMCWEB_LOG_ERROR << "error canceling timer " << ec;
56         }
57         std::string versionInfo;
58         m.read(versionInfo);  // Read in the object path that was just created
59 
60         std::size_t index = versionInfo.rfind('/');
61         if (index != std::string::npos) {
62           versionInfo.erase(0, index);
63         }
64         res.jsonValue = {{"data", std::move(versionInfo)},
65                          {"message", "200 OK"},
66                          {"status", "ok"}};
67         BMCWEB_LOG_DEBUG << "ending response";
68         res.end();
69         fwUpdateMatcher = nullptr;
70       };
71   fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
72       *crow::connections::systemBus,
73       "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
74       "member='InterfacesAdded',path='/xyz/openbmc_project/logging'",
75       callback);
76 
77   std::string filepath(
78       "/tmp/images/" +
79       boost::uuids::to_string(boost::uuids::random_generator()()));
80   BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
81   std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
82                                   std::ofstream::trunc);
83   out << req.body;
84   out.close();
85 }
86 
87 template <typename... Middlewares>
88 void requestRoutes(Crow<Middlewares...>& app) {
89   BMCWEB_ROUTE(app, "/upload/image/<str>")
90       .methods("POST"_method,
91                "PUT"_method)([](const crow::Request& req, crow::Response& res,
92                                 const std::string& filename) {
93         uploadImageHandler(req, res, filename);
94       });
95 
96   BMCWEB_ROUTE(app, "/upload/image")
97       .methods("POST"_method,
98                "PUT"_method)([](const crow::Request& req, crow::Response& res) {
99         uploadImageHandler(req, res, "");
100       });
101 }
102 }  // namespace image_upload
103 }  // namespace crow
104