xref: /openbmc/bmcweb/include/image_upload.hpp (revision e01d0c36af115ed46d54b5dbbacfe3ad92226bd3)
1 #pragma once
2 
3 #include "app.hpp"
4 #include "dbus_singleton.hpp"
5 #include "dbus_utility.hpp"
6 #include "ossl_random.hpp"
7 
8 #include <sdbusplus/bus/match.hpp>
9 
10 #include <cstdio>
11 #include <fstream>
12 #include <memory>
13 
14 namespace crow
15 {
16 namespace image_upload
17 {
18 
19 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
20 static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
21 
22 inline void
23     uploadImageHandler(const crow::Request& req,
24                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
25 {
26     // Only allow one FW update at a time
27     if (fwUpdateMatcher != nullptr)
28     {
29         asyncResp->res.addHeader("Retry-After", "30");
30         asyncResp->res.result(boost::beast::http::status::service_unavailable);
31         return;
32     }
33     // Make this const static so it survives outside this method
34     static boost::asio::steady_timer timeout(*req.ioService,
35                                              std::chrono::seconds(5));
36 
37     timeout.expires_after(std::chrono::seconds(15));
38 
39     auto timeoutHandler = [asyncResp](const boost::system::error_code& ec) {
40         fwUpdateMatcher = nullptr;
41         if (ec == boost::asio::error::operation_aborted)
42         {
43             // expected, we were canceled before the timer completed.
44             return;
45         }
46         BMCWEB_LOG_ERROR("Timed out waiting for Version interface");
47 
48         if (ec)
49         {
50             BMCWEB_LOG_ERROR("Async_wait failed {}", ec);
51             return;
52         }
53 
54         asyncResp->res.result(boost::beast::http::status::bad_request);
55         asyncResp->res.jsonValue["data"]["description"] =
56             "Version already exists or failed to be extracted";
57         asyncResp->res.jsonValue["message"] = "400 Bad Request";
58         asyncResp->res.jsonValue["status"] = "error";
59     };
60 
61     std::function<void(sdbusplus::message_t&)> callback =
62         [asyncResp](sdbusplus::message_t& m) {
63         BMCWEB_LOG_DEBUG("Match fired");
64 
65         sdbusplus::message::object_path path;
66         dbus::utility::DBusInteracesMap interfaces;
67         m.read(path, interfaces);
68 
69         if (std::find_if(interfaces.begin(), interfaces.end(),
70                          [](const auto& i) {
71             return i.first == "xyz.openbmc_project.Software.Version";
72             }) != interfaces.end())
73         {
74             timeout.cancel();
75             std::string leaf = path.filename();
76             if (leaf.empty())
77             {
78                 leaf = path.str;
79             }
80 
81             asyncResp->res.jsonValue["data"] = leaf;
82             asyncResp->res.jsonValue["message"] = "200 OK";
83             asyncResp->res.jsonValue["status"] = "ok";
84             BMCWEB_LOG_DEBUG("ending response");
85             fwUpdateMatcher = nullptr;
86         }
87     };
88     fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
89         *crow::connections::systemBus,
90         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
91         "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
92         callback);
93 
94     std::string filepath("/tmp/images/" + bmcweb::getRandomUUID());
95     BMCWEB_LOG_DEBUG("Writing file to {}", filepath);
96     std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
97                                     std::ofstream::trunc);
98     out << req.body();
99     out.close();
100     timeout.async_wait(timeoutHandler);
101 }
102 
103 inline void requestRoutes(App& app)
104 {
105     BMCWEB_ROUTE(app, "/upload/image/<str>")
106         .privileges({{"ConfigureComponents", "ConfigureManager"}})
107         .methods(boost::beast::http::verb::post, boost::beast::http::verb::put)(
108             [](const crow::Request& req,
109                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
110                const std::string&) { uploadImageHandler(req, asyncResp); });
111 
112     BMCWEB_ROUTE(app, "/upload/image")
113         .privileges({{"ConfigureComponents", "ConfigureManager"}})
114         .methods(boost::beast::http::verb::post, boost::beast::http::verb::put)(
115             [](const crow::Request& req,
116                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
117         uploadImageHandler(req, asyncResp);
118         });
119 }
120 } // namespace image_upload
121 } // namespace crow
122