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