#include "../include/endpoint_manager_base.hpp"
#include <vsomeip/internal/logger.hpp>
#include "../../utility/include/utility.hpp"
#include "../../routing/include/routing_manager_base.hpp"
#include "../../configuration/include/configuration.hpp"
#include "../include/local_tcp_client_endpoint_impl.hpp"
#include "../include/local_tcp_server_endpoint_impl.hpp"
#if defined(__linux__) || defined(ANDROID) || defined(__QNX__)
#include "../include/local_uds_client_endpoint_impl.hpp"
#include "../include/local_uds_server_endpoint_impl.hpp"
#endif
#include <iomanip>
namespace vsomeip_v3 {
endpoint_manager_base::endpoint_manager_base(
routing_manager_base* const _rm,
boost::asio::io_context &_io,
const std::shared_ptr<configuration>& _configuration)
: rm_(_rm),
io_(_io),
configuration_(_configuration),
local_port_(ILLEGAL_PORT) {
is_local_routing_ = configuration_->is_local_routing();
}
std::shared_ptr<endpoint> endpoint_manager_base::create_local(client_t _client) {
std::lock_guard<std::mutex> its_lock(local_endpoint_mutex_);
return create_local_unlocked(_client);
}
void endpoint_manager_base::remove_local(client_t _client) {
std::shared_ptr<endpoint> its_endpoint(find_local(_client));
if (its_endpoint) {
its_endpoint->register_error_handler(nullptr);
its_endpoint->stop();
VSOMEIP_INFO << "Client [" << std::hex << rm_->get_client() << "] is closing connection to ["
<< std::hex << _client << "]";
std::lock_guard<std::mutex> its_lock(local_endpoint_mutex_);
local_endpoints_.erase(_client);
}
}
std::shared_ptr<endpoint> endpoint_manager_base::find_or_create_local(client_t _client) {
std::lock_guard<std::mutex> its_lock(local_endpoint_mutex_);
std::shared_ptr<endpoint> its_endpoint(find_local_unlocked(_client));
if (!its_endpoint) {
its_endpoint = create_local_unlocked(_client);
its_endpoint->start();
}
return its_endpoint;
}
std::shared_ptr<endpoint> endpoint_manager_base::find_local(client_t _client) {
std::lock_guard<std::mutex> its_lock(local_endpoint_mutex_);
return find_local_unlocked(_client);
}
std::shared_ptr<endpoint> endpoint_manager_base::find_local(service_t _service,
instance_t _instance) {
return find_local(rm_->find_local_client(_service, _instance));
}
std::unordered_set<client_t> endpoint_manager_base::get_connected_clients() const {
std::lock_guard<std::mutex> its_lock(local_endpoint_mutex_);
std::unordered_set<client_t> clients;
for (const auto& its_client : local_endpoints_) {
clients.insert(its_client.first);
}
return clients;
}
std::shared_ptr<endpoint> endpoint_manager_base::create_local_server(
const std::shared_ptr<routing_host> &_routing_host) {
std::shared_ptr<endpoint> its_server_endpoint;
std::stringstream its_path;
its_path << utility::get_base_path(configuration_->get_network())
<< std::hex << rm_->get_client();
const client_t its_client = rm_->get_client();
#if defined(__linux__) || defined(ANDROID) || defined(__QNX__)
if (is_local_routing_) {
if (-1 == ::unlink(its_path.str().c_str()) && errno != ENOENT) {
VSOMEIP_ERROR << "endpoint_manager_base::init_receiver unlink failed ("
<< its_path.str() << "): "<< std::strerror(errno);
}
try {
its_server_endpoint = std::make_shared<local_uds_server_endpoint_impl>(
shared_from_this(), _routing_host,
# if VSOMEIP_BOOST_VERSION < 106600
boost::asio::local::stream_protocol_ext::endpoint(its_path.str()),
# else
boost::asio::local::stream_protocol::endpoint(its_path.str()),
# endif
io_,
configuration_, false);
VSOMEIP_INFO << __func__ << ": Listening @ " << its_path.str();
} catch (const std::exception &e) {
VSOMEIP_ERROR << "Local UDS server endpoint creation failed. Client "
<< std::hex << std::setw(4) << std::setfill('0') << its_client
<< " Path: " << its_path.str()
<< " Reason: " << e.what();
}
} else {
#else
{
#endif
std::lock_guard<std::mutex> its_lock(create_local_server_endpoint_mutex_);
::unlink(its_path.str().c_str());
port_t its_port;
std::set<port_t> its_used_ports;
auto its_address = configuration_->get_routing_guest_address();
uint32_t its_current_wait_time { 0 };
while (get_local_server_port(its_port, its_used_ports) && !its_server_endpoint) {
try {
its_server_endpoint = std::make_shared<local_tcp_server_endpoint_impl>(
shared_from_this(), _routing_host,
boost::asio::ip::tcp::endpoint(its_address, its_port),
io_,
configuration_, false);
VSOMEIP_INFO << __func__ << ": Listening @ "
<< its_address.to_string() << ":" << std::dec << its_port;
if (rm_->is_routing_manager())
local_port_ = port_t(configuration_->get_routing_host_port() + 1);
else
local_port_ = port_t(its_port + 1);
VSOMEIP_INFO << __func__ << ": Connecting to other clients from "
<< its_address.to_string() << ":" << std::dec << local_port_;
rm_->set_sec_client_port(local_port_);
} catch (const boost::system::system_error &e) {
if (e.code() == boost::asio::error::address_in_use) {
its_used_ports.insert(its_port);
} else {
its_current_wait_time += LOCAL_TCP_PORT_WAIT_TIME;
if (its_current_wait_time > LOCAL_TCP_PORT_MAX_WAIT_TIME)
break;
std::this_thread::sleep_for(
std::chrono::milliseconds(LOCAL_TCP_PORT_WAIT_TIME));
}
}
}
if (!its_server_endpoint) {
VSOMEIP_ERROR << "Local TCP server endpoint creation failed. Client "
<< std::hex << std::setw(4) << std::setfill('0') << its_client
<< " Reason: No local port available!";
} else {
rm_->add_guest(its_client, its_address, its_port);
}
}
return its_server_endpoint;
}
void endpoint_manager_base::on_connect(std::shared_ptr<endpoint> _endpoint) {
rm_->on_connect(_endpoint);
}
void endpoint_manager_base::on_disconnect(std::shared_ptr<endpoint> _endpoint) {
rm_->on_disconnect(_endpoint);
}
bool endpoint_manager_base::on_bind_error(std::shared_ptr<endpoint> _endpoint,
const boost::asio::ip::address &_remote_address,
uint16_t _remote_port) {
(void)_endpoint;
(void)_remote_address;
(void)_remote_port;
return true;
}
void endpoint_manager_base::on_error(
const byte_t *_data, length_t _length, endpoint* const _receiver,
const boost::asio::ip::address &_remote_address,
std::uint16_t _remote_port) {
(void)_data;
(void)_length;
(void)_receiver;
(void)_remote_address;
(void)_remote_port;
}
void endpoint_manager_base::release_port(uint16_t _port, bool _reliable) {
(void)_port;
(void)_reliable;
}
client_t endpoint_manager_base::get_client() const {
return rm_->get_client();
}
std::string endpoint_manager_base::get_client_host() const {
return rm_->get_client_host();
}
std::map<client_t, std::shared_ptr<endpoint>>
endpoint_manager_base::get_local_endpoints() const {
std::lock_guard<std::mutex> its_lock(local_endpoint_mutex_);
return local_endpoints_;
}
void
endpoint_manager_base::log_client_states() const {
std::vector<std::pair<client_t, size_t> > its_client_queue_sizes;
std::stringstream its_log;
{
std::lock_guard<std::mutex> its_lock(local_endpoint_mutex_);
for (const auto &e : local_endpoints_) {
size_t its_queue_size = e.second->get_queue_size();
if (its_queue_size > VSOMEIP_DEFAULT_QUEUE_WARN_SIZE) {
its_client_queue_sizes.push_back(
std::make_pair(e.first, its_queue_size));
}
}
}
std::sort(its_client_queue_sizes.begin(), its_client_queue_sizes.end(),
[](const std::pair<client_t, size_t> &_a,
const std::pair<client_t, size_t> &_b) {
return (_a.second > _b.second);
});
size_t its_max(std::min(size_t(10), its_client_queue_sizes.size()));
its_log << std::setfill('0');
for (size_t i = 0; i < its_max; i++) {
its_log << std::hex << std::setw(4) << its_client_queue_sizes[i].first << ":"
<< std::dec << its_client_queue_sizes[i].second;
if (i < its_max-1)
its_log << ", ";
}
if (its_log.str().length() > 0)
VSOMEIP_WARNING << "ICQ: [" << its_log.str() << "]";
}
std::shared_ptr<endpoint>
endpoint_manager_base::create_local_unlocked(client_t _client) {
std::stringstream its_path;
its_path << utility::get_base_path(configuration_->get_network())
<< std::hex << _client;
std::shared_ptr<endpoint> its_endpoint;
#if defined(__linux__) || defined(ANDROID) || defined(__QNX__)
if (is_local_routing_) {
its_endpoint = std::make_shared<local_uds_client_endpoint_impl>(
shared_from_this(), rm_->shared_from_this(),
boost::asio::local::stream_protocol::endpoint(its_path.str()),
io_, configuration_);
VSOMEIP_INFO << "Client [" << std::hex << rm_->get_client() << "] is connecting to ["
<< std::hex << _client << "] at " << its_path.str();
} else {
#else
{
#endif
boost::asio::ip::address its_local_address, its_remote_address;
port_t its_remote_port;
bool is_guest = rm_->get_guest(_client, its_remote_address, its_remote_port);
if (is_guest) {
try {
its_local_address = configuration_->get_routing_guest_address();
its_endpoint = std::make_shared<local_tcp_client_endpoint_impl>(
shared_from_this(), rm_->shared_from_this(),
boost::asio::ip::tcp::endpoint(its_local_address, local_port_),
boost::asio::ip::tcp::endpoint(its_remote_address, its_remote_port),
io_, configuration_);
VSOMEIP_INFO << "Client ["
<< std::hex << std::setw(4) << std::setfill('0') << rm_->get_client()
<< "] @ "
<< its_local_address.to_string() << ":" << std::dec << local_port_
<< " is connecting to ["
<< std::hex << std::setw(4) << std::setfill('0') << _client << "] @ "
<< its_remote_address.to_string() << ":" << std::dec << its_remote_port;
} catch (...) {
}
} else {
VSOMEIP_ERROR << __func__
<< ": Cannot get guest address of client ["
<< std::hex << std::setw(4) << std::setfill('0')
<< _client << "]";
}
}
if (its_endpoint) {
if (_client != VSOMEIP_ROUTING_CLIENT) {
local_endpoints_[_client] = its_endpoint;
}
rm_->register_client_error_handler(_client, its_endpoint);
}
return its_endpoint;
}
std::shared_ptr<endpoint> endpoint_manager_base::find_local_unlocked(client_t _client) {
std::shared_ptr<endpoint> its_endpoint;
auto found_endpoint = local_endpoints_.find(_client);
if (found_endpoint != local_endpoints_.end()) {
its_endpoint = found_endpoint->second;
}
return its_endpoint;
}
instance_t endpoint_manager_base::find_instance(
service_t _service, endpoint* const _endpoint) const {
(void)_service;
(void)_endpoint;
return (0xFFFF);
}
bool
endpoint_manager_base::get_local_server_port(port_t &_port,
const std::set<port_t> &_used_ports) const {
#define SERVER_PORT_OFFSET 2
#ifdef _WIN32
uid_t its_uid { ANY_UID };
gid_t its_gid { ANY_GID };
#else
uid_t its_uid { getuid() };
gid_t its_gid { getgid() };
#endif
auto its_port_ranges = configuration_->get_routing_guest_ports(
its_uid, its_gid);
if (its_port_ranges.empty()) {
VSOMEIP_WARNING << __func__ << ": No configured port ranges for uid/gid="
<< std::dec << its_uid << '/' << its_gid;
}
for (const auto &its_range : its_port_ranges) {
for (int r = its_range.first; r < its_range.second;
r += SERVER_PORT_OFFSET) {
if (_used_ports.find(port_t(r)) == _used_ports.end()
&& r != configuration_->get_routing_host_port()) {
_port = port_t(r);
return true;
}
}
}
return false;
}
void
endpoint_manager_base::add_multicast_option(const multicast_option_t &_option) {
(void)_option;
}
}