blob: f6a0dfc49ecac90674c027aec197af9db95f17eb [file] [log] [blame]
Ed Tanous40e9b922024-09-10 13:50:16 -07001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright OpenBMC Authors
Ed Tanousc3ee5222018-05-01 12:58:27 -07003#pragma once
4
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08005#include "app.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -08006#include "async_resp.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08007#include "dbus_singleton.hpp"
8#include "dbus_utility.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -08009#include "http_request.hpp"
Ed Tanousd98a2f92025-02-06 17:36:31 -080010#include "io_context_singleton.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -080011#include "logging.hpp"
Ed Tanous2c6ffdb2023-06-28 11:28:38 -070012#include "ossl_random.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080013
Ed Tanousd7857202025-01-28 15:32:26 -080014#include <boost/asio/error.hpp>
15#include <boost/asio/steady_timer.hpp>
16#include <boost/beast/http/status.hpp>
17#include <boost/beast/http/verb.hpp>
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080018#include <sdbusplus/bus/match.hpp>
Ed Tanousd7857202025-01-28 15:32:26 -080019#include <sdbusplus/message.hpp>
20#include <sdbusplus/message/native_types.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050021
Ed Tanousd7857202025-01-28 15:32:26 -080022#include <algorithm>
23#include <chrono>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050024#include <cstdio>
Ed Tanous1abe55e2018-09-05 08:30:59 -070025#include <fstream>
Ed Tanousd7857202025-01-28 15:32:26 -080026#include <functional>
Ed Tanous1abe55e2018-09-05 08:30:59 -070027#include <memory>
Ed Tanous3544d2a2023-08-06 18:12:20 -070028#include <ranges>
Ed Tanousd7857202025-01-28 15:32:26 -080029#include <string>
Ed Tanousc3ee5222018-05-01 12:58:27 -070030
Ed Tanous1abe55e2018-09-05 08:30:59 -070031namespace crow
32{
33namespace image_upload
34{
Ed Tanousc3ee5222018-05-01 12:58:27 -070035
Ed Tanouscf9e4172022-12-21 09:30:16 -080036// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams59d494e2022-07-22 19:26:55 -050037static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
Ed Tanousc3ee5222018-05-01 12:58:27 -070038
Patrick Williams504af5a2025-02-03 14:29:03 -050039inline void uploadImageHandler(
40 const crow::Request& req,
41 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -070042{
43 // Only allow one FW update at a time
44 if (fwUpdateMatcher != nullptr)
45 {
zhanghch058d1b46d2021-04-01 11:18:24 +080046 asyncResp->res.addHeader("Retry-After", "30");
47 asyncResp->res.result(boost::beast::http::status::service_unavailable);
Ed Tanous1abe55e2018-09-05 08:30:59 -070048 return;
49 }
50 // Make this const static so it survives outside this method
Ed Tanousd98a2f92025-02-06 17:36:31 -080051 static boost::asio::steady_timer timeout(getIoContext(),
Ed Tanous271584a2019-07-09 16:24:22 -070052 std::chrono::seconds(5));
Ed Tanous1abe55e2018-09-05 08:30:59 -070053
Ed Tanous271584a2019-07-09 16:24:22 -070054 timeout.expires_after(std::chrono::seconds(15));
Ed Tanous1abe55e2018-09-05 08:30:59 -070055
zhanghch058d1b46d2021-04-01 11:18:24 +080056 auto timeoutHandler = [asyncResp](const boost::system::error_code& ec) {
Ed Tanousc3ee5222018-05-01 12:58:27 -070057 fwUpdateMatcher = nullptr;
Ed Tanous23e64202020-09-15 19:21:30 -070058 if (ec == boost::asio::error::operation_aborted)
Ed Tanous1abe55e2018-09-05 08:30:59 -070059 {
60 // expected, we were canceled before the timer completed.
61 return;
62 }
Ed Tanous62598e32023-07-17 17:06:25 -070063 BMCWEB_LOG_ERROR("Timed out waiting for Version interface");
Ed Tanousc3ee5222018-05-01 12:58:27 -070064
Ed Tanous1abe55e2018-09-05 08:30:59 -070065 if (ec)
66 {
Ed Tanous62598e32023-07-17 17:06:25 -070067 BMCWEB_LOG_ERROR("Async_wait failed {}", ec);
Ed Tanous1abe55e2018-09-05 08:30:59 -070068 return;
69 }
70
zhanghch058d1b46d2021-04-01 11:18:24 +080071 asyncResp->res.result(boost::beast::http::status::bad_request);
Ed Tanous14766872022-03-15 10:44:42 -070072 asyncResp->res.jsonValue["data"]["description"] =
73 "Version already exists or failed to be extracted";
74 asyncResp->res.jsonValue["message"] = "400 Bad Request";
75 asyncResp->res.jsonValue["status"] = "error";
Lei YU9f898f82019-03-08 16:52:10 +080076 };
Ed Tanous1abe55e2018-09-05 08:30:59 -070077
Patrick Williams59d494e2022-07-22 19:26:55 -050078 std::function<void(sdbusplus::message_t&)> callback =
79 [asyncResp](sdbusplus::message_t& m) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -040080 BMCWEB_LOG_DEBUG("Match fired");
Ed Tanous1abe55e2018-09-05 08:30:59 -070081
Patrick Williamsbd79bce2024-08-16 15:22:20 -040082 sdbusplus::message::object_path path;
83 dbus::utility::DBusInterfacesMap interfaces;
84 m.read(path, interfaces);
Matt Spinlerc9008502019-01-21 12:21:25 -060085
Patrick Williamsbd79bce2024-08-16 15:22:20 -040086 if (std::ranges::find_if(interfaces, [](const auto& i) {
87 return i.first == "xyz.openbmc_project.Software.Version";
88 }) != interfaces.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -070089 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -040090 timeout.cancel();
91 std::string leaf = path.filename();
92 if (leaf.empty())
93 {
94 leaf = path.str;
95 }
Ed Tanous002d39b2022-05-31 08:59:27 -070096
Patrick Williamsbd79bce2024-08-16 15:22:20 -040097 asyncResp->res.jsonValue["data"] = leaf;
98 asyncResp->res.jsonValue["message"] = "200 OK";
99 asyncResp->res.jsonValue["status"] = "ok";
100 BMCWEB_LOG_DEBUG("ending response");
101 fwUpdateMatcher = nullptr;
102 }
103 };
Patrick Williams59d494e2022-07-22 19:26:55 -0500104 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700105 *crow::connections::systemBus,
106 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
Matt Spinlerc9008502019-01-21 12:21:25 -0600107 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700108 callback);
109
Ed Tanous2c6ffdb2023-06-28 11:28:38 -0700110 std::string filepath("/tmp/images/" + bmcweb::getRandomUUID());
Ed Tanous62598e32023-07-17 17:06:25 -0700111 BMCWEB_LOG_DEBUG("Writing file to {}", filepath);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700112 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
113 std::ofstream::trunc);
Ed Tanous33c6b582023-02-14 15:05:48 -0800114 out << req.body();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700115 out.close();
Lei YU9f898f82019-03-08 16:52:10 +0800116 timeout.async_wait(timeoutHandler);
Ed Tanousc3ee5222018-05-01 12:58:27 -0700117}
118
Ed Tanous23a21a12020-07-25 04:45:05 +0000119inline void requestRoutes(App& app)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700120{
121 BMCWEB_ROUTE(app, "/upload/image/<str>")
Ed Tanous432a8902021-06-14 15:28:56 -0700122 .privileges({{"ConfigureComponents", "ConfigureManager"}})
Ed Tanousb41187f2019-10-24 16:30:02 -0700123 .methods(boost::beast::http::verb::post, boost::beast::http::verb::put)(
zhanghch058d1b46d2021-04-01 11:18:24 +0800124 [](const crow::Request& req,
125 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
126 const std::string&) { uploadImageHandler(req, asyncResp); });
Ed Tanousc3ee5222018-05-01 12:58:27 -0700127
Ed Tanous1abe55e2018-09-05 08:30:59 -0700128 BMCWEB_ROUTE(app, "/upload/image")
Ed Tanous432a8902021-06-14 15:28:56 -0700129 .privileges({{"ConfigureComponents", "ConfigureManager"}})
Ed Tanousb41187f2019-10-24 16:30:02 -0700130 .methods(boost::beast::http::verb::post, boost::beast::http::verb::put)(
zhanghch058d1b46d2021-04-01 11:18:24 +0800131 [](const crow::Request& req,
132 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400133 uploadImageHandler(req, asyncResp);
134 });
Ed Tanousc3ee5222018-05-01 12:58:27 -0700135}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700136} // namespace image_upload
137} // namespace crow