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