xref: /openbmc/bmcweb/include/image_upload.hpp (revision 141d9431)
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::match> 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 = {
55             {"data",
56              {{"description",
57                "Version already exists or failed to be extracted"}}},
58             {"message", "400 Bad Request"},
59             {"status", "error"}};
60     };
61 
62     std::function<void(sdbusplus::message::message&)> callback =
63         [asyncResp](sdbusplus::message::message& m) {
64             BMCWEB_LOG_DEBUG << "Match fired";
65 
66             sdbusplus::message::object_path path;
67             std::vector<std::pair<
68                 std::string, std::vector<std::pair<
69                                  std::string, dbus::utility::DbusVariantType>>>>
70                 interfaces;
71             m.read(path, interfaces);
72 
73             if (std::find_if(interfaces.begin(), interfaces.end(),
74                              [](const auto& i) {
75                                  return i.first ==
76                                         "xyz.openbmc_project.Software.Version";
77                              }) != interfaces.end())
78             {
79                 timeout.cancel();
80                 std::string leaf = path.filename();
81                 if (leaf.empty())
82                 {
83                     leaf = path.str;
84                 }
85 
86                 asyncResp->res.jsonValue = {
87                     {"data", leaf}, {"message", "200 OK"}, {"status", "ok"}};
88                 BMCWEB_LOG_DEBUG << "ending response";
89                 fwUpdateMatcher = nullptr;
90             }
91         };
92     fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
93         *crow::connections::systemBus,
94         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
95         "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
96         callback);
97 
98     std::string filepath(
99         "/tmp/images/" +
100         boost::uuids::to_string(boost::uuids::random_generator()()));
101     BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
102     std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
103                                     std::ofstream::trunc);
104     out << req.body;
105     out.close();
106     timeout.async_wait(timeoutHandler);
107 }
108 
109 inline void requestRoutes(App& app)
110 {
111     BMCWEB_ROUTE(app, "/upload/image/<str>")
112         .privileges({{"ConfigureComponents", "ConfigureManager"}})
113         .methods(boost::beast::http::verb::post, boost::beast::http::verb::put)(
114             [](const crow::Request& req,
115                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
116                const std::string&) { uploadImageHandler(req, asyncResp); });
117 
118     BMCWEB_ROUTE(app, "/upload/image")
119         .privileges({{"ConfigureComponents", "ConfigureManager"}})
120         .methods(boost::beast::http::verb::post, boost::beast::http::verb::put)(
121             [](const crow::Request& req,
122                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
123                 uploadImageHandler(req, asyncResp);
124             });
125 }
126 } // namespace image_upload
127 } // namespace crow
128