xref: /openbmc/bmcweb/include/image_upload.hpp (revision 74ab83c5)
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 #include <dbus_utility.hpp>
9 
10 #include <cstdio>
11 #include <fstream>
12 #include <memory>
13 
14 namespace crow
15 {
16 namespace image_upload
17 {
18 
19 static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
20 
21 inline void
22     uploadImageHandler(const crow::Request& req,
23                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
24 {
25     // Only allow one FW update at a time
26     if (fwUpdateMatcher != nullptr)
27     {
28         asyncResp->res.addHeader("Retry-After", "30");
29         asyncResp->res.result(boost::beast::http::status::service_unavailable);
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 = [asyncResp](const boost::system::error_code& ec) {
39         fwUpdateMatcher = nullptr;
40         if (ec == boost::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         asyncResp->res.result(boost::beast::http::status::bad_request);
54         asyncResp->res.jsonValue["data"]["description"] =
55             "Version already exists or failed to be extracted";
56         asyncResp->res.jsonValue["message"] = "400 Bad Request";
57         asyncResp->res.jsonValue["status"] = "error";
58     };
59 
60     std::function<void(sdbusplus::message_t&)> callback =
61         [asyncResp](sdbusplus::message_t& m) {
62         BMCWEB_LOG_DEBUG << "Match fired";
63 
64         sdbusplus::message::object_path path;
65         dbus::utility::DBusInteracesMap interfaces;
66         m.read(path, interfaces);
67 
68         if (std::find_if(interfaces.begin(), interfaces.end(),
69                          [](const auto& i) {
70             return i.first == "xyz.openbmc_project.Software.Version";
71             }) != interfaces.end())
72         {
73             timeout.cancel();
74             std::string leaf = path.filename();
75             if (leaf.empty())
76             {
77                 leaf = path.str;
78             }
79 
80             asyncResp->res.jsonValue["data"] = leaf;
81             asyncResp->res.jsonValue["message"] = "200 OK";
82             asyncResp->res.jsonValue["status"] = "ok";
83             BMCWEB_LOG_DEBUG << "ending response";
84             fwUpdateMatcher = nullptr;
85         }
86     };
87     fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
88         *crow::connections::systemBus,
89         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
90         "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
91         callback);
92 
93     std::string filepath(
94         "/tmp/images/" +
95         boost::uuids::to_string(boost::uuids::random_generator()()));
96     BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
97     std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
98                                     std::ofstream::trunc);
99     out << req.body;
100     out.close();
101     timeout.async_wait(timeoutHandler);
102 }
103 
104 inline void requestRoutes(App& app)
105 {
106     BMCWEB_ROUTE(app, "/upload/image/<str>")
107         .privileges({{"ConfigureComponents", "ConfigureManager"}})
108         .methods(boost::beast::http::verb::post, boost::beast::http::verb::put)(
109             [](const crow::Request& req,
110                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
111                const std::string&) { uploadImageHandler(req, asyncResp); });
112 
113     BMCWEB_ROUTE(app, "/upload/image")
114         .privileges({{"ConfigureComponents", "ConfigureManager"}})
115         .methods(boost::beast::http::verb::post, boost::beast::http::verb::put)(
116             [](const crow::Request& req,
117                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
118         uploadImageHandler(req, asyncResp);
119         });
120 }
121 } // namespace image_upload
122 } // namespace crow
123