1 #include <boost/asio/deadline_timer.hpp>
2 #include <boost/asio/io_service.hpp>
3 #include <boost/asio/ip/tcp.hpp>
4 #include <boost/asio/placeholders.hpp>
5 #include <boost/bind.hpp>
8 #include <unordered_map>
9 #include <unordered_set>
11 #include "ipuptanesecondary.h"
12 #include "logging/logging.h"
13 #include "secondary.h"
14 #include "secondary_config.h"
15 #include "utilities/utils.h"
19 using Secondaries = std::vector<std::shared_ptr<SecondaryInterface>>;
20 using SecondaryFactoryRegistry =
21 std::unordered_map<std::string, std::function<Secondaries(
const SecondaryConfig&,
Aktualizr& aktualizr)>>;
23 static Secondaries createIPSecondaries(
const IPSecondariesConfig& config,
Aktualizr& aktualizr);
26 static SecondaryFactoryRegistry sec_factory_registry = {
27 {IPSecondariesConfig::Type,
28 [](
const SecondaryConfig& config,
Aktualizr& aktualizr) {
29 auto ip_sec_cgf =
dynamic_cast<const IPSecondariesConfig&
>(config);
30 return createIPSecondaries(ip_sec_cgf, aktualizr);
32 {VirtualSecondaryConfig::Type,
33 [](
const SecondaryConfig& config,
Aktualizr& aktualizr) {
35 auto virtual_sec_cgf =
dynamic_cast<const VirtualSecondaryConfig&
>(config);
36 return Secondaries({std::make_shared<VirtualSecondary>(virtual_sec_cgf)});
43 static Secondaries createSecondaries(
const SecondaryConfig& config,
Aktualizr& aktualizr) {
44 return (sec_factory_registry.at(config.type()))(config, aktualizr);
47 void initSecondaries(
Aktualizr& aktualizr,
const boost::filesystem::path& config_file) {
48 if (!boost::filesystem::exists(config_file)) {
49 throw std::invalid_argument(
"Secondary ECUs config file does not exist: " + config_file.string());
52 auto secondary_configs = SecondaryConfigParser::parse_config_file(config_file);
54 for (
auto& config : secondary_configs) {
56 LOG_INFO <<
"Initializing " << config->type() <<
" Secondaries...";
57 Secondaries secondaries = createSecondaries(*config, aktualizr);
59 for (
const auto& secondary : secondaries) {
60 LOG_INFO <<
"Adding Secondary with ECU serial: " << secondary->getSerial()
61 <<
" with hardware ID: " << secondary->getHwId();
64 }
catch (
const std::exception& exc) {
65 LOG_ERROR <<
"Failed to initialize a Secondary: " << exc.what();
74 : aktualizr_(aktualizr),
75 endpoint_{boost::asio::ip::tcp::v4(), wait_port},
76 timeout_{
static_cast<boost::posix_time::seconds
>(timeout_s)},
78 connected_secondaries_{secondaries} {}
80 void addSecondary(
const std::string& ip, uint16_t port) { secondaries_to_wait_for_.insert(key(ip, port)); }
83 if (secondaries_to_wait_for_.empty()) {
87 timer_.expires_from_now(timeout_);
88 timer_.async_wait([&](
const boost::system::error_code& error_code) {
90 LOG_ERROR <<
"Wait for Secondaries has failed: " << error_code;
91 throw std::runtime_error(
"Error while waiting for IP Secondaries");
93 LOG_ERROR <<
"Timeout while waiting for Secondaries: " << error_code;
94 throw std::runtime_error(
"Timeout while waiting for IP Secondaries");
104 LOG_INFO <<
"Waiting for connection from " << secondaries_to_wait_for_.size() <<
" Secondaries...";
105 acceptor_.async_accept(con_socket_,
106 boost::bind(&SecondaryWaiter::connectionHdlr,
this, boost::asio::placeholders::error));
109 void connectionHdlr(
const boost::system::error_code& error_code) {
111 auto sec_ip = con_socket_.remote_endpoint().address().to_string();
112 auto sec_port = con_socket_.remote_endpoint().port();
114 LOG_INFO <<
"Accepted connection from a Secondary: (" << sec_ip <<
":" << sec_port <<
")";
116 auto secondary = Uptane::IpUptaneSecondary::create(sec_ip, sec_port, con_socket_.native_handle());
118 connected_secondaries_.push_back(secondary);
122 d[
"port"] = sec_port;
123 aktualizr_.
SetSecondaryData(secondary->getSerial(), Utils::jsonToCanonicalStr(d));
125 }
catch (
const std::exception& exc) {
126 LOG_ERROR <<
"Failed to initialize a Secondary: " << exc.what();
128 con_socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
131 secondaries_to_wait_for_.erase(key(sec_ip, sec_port));
132 if (!secondaries_to_wait_for_.empty()) {
138 LOG_ERROR <<
"Failed to accept connection from a Secondary";
142 static std::string key(
const std::string& ip, uint16_t port) {
return (ip + std::to_string(port)); }
146 boost::asio::io_service io_context_;
147 boost::asio::ip::tcp::endpoint endpoint_;
148 boost::asio::ip::tcp::acceptor acceptor_{io_context_, endpoint_};
149 boost::asio::ip::tcp::socket con_socket_{io_context_};
150 boost::posix_time::seconds timeout_;
151 boost::asio::deadline_timer timer_;
153 Secondaries& connected_secondaries_;
154 std::unordered_set<std::string> secondaries_to_wait_for_;
165 Secondaries new_secondaries;
166 SecondaryWaiter sec_waiter{aktualizr, config.secondaries_wait_port, config.secondaries_timeout_s,
result};
169 for (
const auto& cfg : config.secondaries_cfg) {
170 SecondaryInterface::Ptr secondary;
174 auto f = std::find_if(secondaries_info.cbegin(), secondaries_info.cend(), [&cfg](
const SecondaryInfo& i) {
175 Json::Value d = Utils::parseJSON(i.extra);
176 return d[
"ip"] == cfg.ip && d[
"port"] == cfg.port;
179 if (f == secondaries_info.cend() && config.secondaries_cfg.size() == 1 && secondaries_info.size() == 1 &&
180 secondaries_info[0].extra.empty()) {
184 info = &secondaries_info[0];
187 d[
"port"] = cfg.port;
189 LOG_INFO <<
"Migrated a single IP Secondary to new storage format.";
190 }
else if (f == secondaries_info.cend()) {
192 secondary = Uptane::IpUptaneSecondary::connectAndCreate(cfg.ip, cfg.port);
193 if (secondary ==
nullptr) {
194 LOG_DEBUG <<
"Could not connect to IP Secondary at " << cfg.ip <<
":" << cfg.port
195 <<
"; now trying to wait for it.";
196 sec_waiter.addSecondary(cfg.ip, cfg.port);
198 result.push_back(secondary);
202 d[
"port"] = cfg.port;
203 aktualizr.
SetSecondaryData(secondary->getSerial(), Utils::jsonToCanonicalStr(d));
211 if (secondary ==
nullptr) {
213 Uptane::IpUptaneSecondary::connectAndCheck(cfg.ip, cfg.port, info->serial, info->hw_id, info->pub_key);
214 if (secondary ==
nullptr) {
215 throw std::runtime_error(
"Unable to connect to or verify IP Secondary at " + cfg.ip +
":" +
216 std::to_string(cfg.port));
220 result.push_back(secondary);