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