xref: /openbmc/bmcweb/include/image_upload.hpp (revision d45d2d0f)
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