Files
hailort/hailort/common/os/posix/socket.cpp
HailoRT-Automation 3d67325209 v4.19.0 (#21)
2024-09-29 11:29:10 +03:00

368 lines
11 KiB
C++

/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file socket.cpp
* @brief Socket wrapper for Unix
**/
#include "common/socket.hpp"
#include <arpa/inet.h>
#include <unistd.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <array>
namespace hailort
{
#define LINUX_RMEM_MAX_PATH "/proc/sys/net/core/rmem_max"
hailo_status Socket::SocketModuleWrapper::init_module()
{
return HAILO_SUCCESS;
}
hailo_status Socket::SocketModuleWrapper::free_module()
{
return HAILO_SUCCESS;
}
Expected<Socket> Socket::create(int af, int type, int protocol)
{
TRY(auto module_wrapper, SocketModuleWrapper::create());
auto module_wrapper_ptr = make_shared_nothrow<SocketModuleWrapper>(std::move(module_wrapper));
CHECK_NOT_NULL(module_wrapper_ptr, HAILO_OUT_OF_HOST_MEMORY);
TRY(const auto socket_fd, create_socket_fd(af, type, protocol));
auto obj = Socket(module_wrapper_ptr, socket_fd);
return obj;
}
Socket::Socket(std::shared_ptr<SocketModuleWrapper> module_wrapper, const socket_t socket_fd) :
m_module_wrapper(std::move(module_wrapper)), m_socket_fd(socket_fd)
{
}
Socket::~Socket()
{
auto status = close_socket_fd();
if (HAILO_SUCCESS != status) {
LOGGER__ERROR("Failed to free socket fd with status {}", status);
}
}
Expected<socket_t> Socket::create_socket_fd(int af, int type, int protocol)
{
socket_t local_socket = INVALID_SOCKET;
local_socket = socket(af, type, protocol);
CHECK_VALID_SOCKET_AS_EXPECTED(local_socket);
return local_socket;
}
hailo_status Socket::close_socket_fd()
{
if (INVALID_SOCKET != m_socket_fd) {
int socket_rc = close(m_socket_fd);
CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Failed to close socket. errno={}", errno);
}
return HAILO_SUCCESS;
}
hailo_status Socket::abort()
{
int socket_rc = shutdown(m_socket_fd, SHUT_RDWR);
CHECK((0 == socket_rc) || ((-1 == socket_rc) && (ENOTCONN == errno)), HAILO_ETH_FAILURE, "Failed to shutdown (abort) socket. errno={}", errno);
return HAILO_SUCCESS;
}
hailo_status Socket::socket_bind(const sockaddr *addr, socklen_t len)
{
int socket_rc = SOCKET_ERROR;
CHECK_ARG_NOT_NULL(addr);
socket_rc = bind(m_socket_fd, addr, len);
CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Failed to bind socket. errno={}", errno);
return HAILO_SUCCESS;
}
hailo_status Socket::get_sock_name(sockaddr *addr, socklen_t *len)
{
int socket_rc = SOCKET_ERROR;
CHECK_ARG_NOT_NULL(addr);
CHECK_ARG_NOT_NULL(len);
socket_rc = getsockname(m_socket_fd, addr, len);
CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Failed getsockname. errno={}", errno);
return HAILO_SUCCESS;
}
hailo_status Socket::listen(int backlog)
{
auto res = ::listen(m_socket_fd, backlog);
CHECK(0 == res, HAILO_ETH_FAILURE, "Failed to listen on socket. errno={}", errno);
return HAILO_SUCCESS;
}
Expected<Socket> Socket::accept()
{
auto client_socket = ::accept(m_socket_fd, nullptr, nullptr);
CHECK(client_socket != INVALID_SOCKET, make_unexpected(HAILO_ETH_FAILURE), "Failed to accept connection {}", errno);
return Socket(m_module_wrapper, client_socket);
}
hailo_status Socket::connect(const sockaddr *addr, socklen_t len)
{
int ret = ::connect(m_socket_fd, addr, len);
CHECK(0 == ret, HAILO_ETH_FAILURE, "Failed to connect to socket {}", errno);
return HAILO_SUCCESS;
}
Expected<size_t> Socket::recv(uint8_t *buffer, size_t size, int flags)
{
auto read_bytes = ::recv(m_socket_fd, buffer, size, flags);
CHECK(read_bytes >= 0, make_unexpected(HAILO_ETH_FAILURE), "Failed to read from socket {}", errno);
return Expected<size_t>(read_bytes);
}
Expected<size_t> Socket::send(const uint8_t *buffer, size_t size, int flags)
{
auto bytes_written = ::send(m_socket_fd, buffer, size, flags);
CHECK(bytes_written >= 0, make_unexpected(HAILO_ETH_FAILURE), "Failed to write to socket {}", errno);
return Expected<size_t>(bytes_written);
}
hailo_status Socket::sendall(const uint8_t *buffer, size_t size, int flags)
{
size_t offset = 0;
while (offset < size) {
const auto size_to_write = size - offset;
TRY(auto bytes_written, send(buffer + offset, size_to_write, flags));
if (bytes_written == 0) {
return HAILO_ETH_SEND_FAILURE;
}
offset += bytes_written;
}
return HAILO_SUCCESS;
}
hailo_status Socket::ntop(int af, const void *src, char *dst, socklen_t size)
{
CHECK_ARG_NOT_NULL(src);
CHECK_ARG_NOT_NULL(dst);
CHECK(NULL != inet_ntop(af, src, dst, size), HAILO_ETH_FAILURE,
"Could not convert sockaddr struct to string ip address");
return HAILO_SUCCESS;
}
hailo_status Socket::pton(int af, const char *src, void *dst)
{
int inet_rc = 0;
CHECK_ARG_NOT_NULL(src);
CHECK_ARG_NOT_NULL(dst);
inet_rc = inet_pton(af, reinterpret_cast<const char*>(src), dst);
if (1 != inet_rc) {
return HAILO_ETH_FAILURE;
}
return HAILO_SUCCESS;
}
hailo_status Socket::set_recv_buffer_size_max()
{
int socket_rc = SOCKET_ERROR;
FILE *rmem_max_file = NULL;
uint8_t rmem_max_buffer[20] = {};
uint64_t rmem_max = 0;
int file_status = 0;
size_t bytes_read = 0;
rmem_max_file = fopen(LINUX_RMEM_MAX_PATH, "r");
if (NULL != rmem_max_file) {
bytes_read = fread(rmem_max_buffer, sizeof(rmem_max_buffer), sizeof(*rmem_max_buffer), rmem_max_file);
if ((0 != bytes_read) || (feof(rmem_max_file))) {
rmem_max = strtoul((char *)rmem_max_buffer, NULL, 10);
}
if (0 == rmem_max) {
LOGGER__WARN("Could not read rmem_max value from file '{}'", LINUX_RMEM_MAX_PATH);
rmem_max = UINT64_MAX;
}
file_status = fclose(rmem_max_file);
if (0 != file_status) {
LOGGER__WARN("Could not close file '{}' errno - {}.", LINUX_RMEM_MAX_PATH, errno);
}
} else {
LOGGER__WARN("Could not open file '{}' to read rmem_max value.", LINUX_RMEM_MAX_PATH);
}
socket_rc = setsockopt(m_socket_fd, SOL_SOCKET, SO_RCVBUF, &rmem_max, sizeof(rmem_max));
CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Cannot set the rcv socket buffer to {}", rmem_max);
return HAILO_SUCCESS;
}
hailo_status Socket::set_timeout(std::chrono::milliseconds timeout_ms, timeval_t *timeout)
{
int socket_rc = SOCKET_ERROR;
time_t seconds = 0;
suseconds_t microseconds = 0;
auto timeout_value = static_cast<uint32_t>(timeout_ms.count());
/* Validate arguments */
CHECK_ARG_NOT_NULL(timeout);
seconds = (timeout_value / MILLISECONDS_IN_SECOND);
microseconds = (timeout_value % MILLISECONDS_IN_SECOND) * MICROSECONDS_IN_MILLISECOND;
timeout->tv_sec = seconds;
timeout->tv_usec = microseconds;
socket_rc = setsockopt(m_socket_fd, SOL_SOCKET, SO_RCVTIMEO, timeout, sizeof(*timeout));
CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Cannot set receive timeout. Seconds: {}, microseconds {}", seconds,
microseconds);
socket_rc = setsockopt(m_socket_fd, SOL_SOCKET, SO_SNDTIMEO, timeout, sizeof(*timeout));
CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Cannot set send timeout. Seconds: {}, microseconds {}", seconds,
microseconds);
return HAILO_SUCCESS;
}
hailo_status Socket::enable_broadcast()
{
int socket_rc = SOCKET_ERROR;
int enable_broadcast = 1;
socket_rc = setsockopt(m_socket_fd, SOL_SOCKET, SO_BROADCAST, &enable_broadcast, sizeof(enable_broadcast));
CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Cannot set socket to be broadcast");
return HAILO_SUCCESS;
}
hailo_status Socket::allow_reuse_address()
{
int allow_reuse = 1;
auto socket_rc = setsockopt(m_socket_fd, SOL_SOCKET, SO_REUSEADDR, &allow_reuse, sizeof(allow_reuse));
CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Cannot set socket to be broadcast");
return HAILO_SUCCESS;
}
hailo_status Socket::send_to(const uint8_t *src_buffer, size_t src_buffer_size, int flags,
const sockaddr *dest_addr, socklen_t dest_addr_size, size_t *bytes_sent)
{
ssize_t number_of_sent_bytes = 0;
/* Validate arguments */
CHECK_ARG_NOT_NULL(src_buffer);
CHECK_ARG_NOT_NULL(dest_addr);
CHECK_ARG_NOT_NULL(bytes_sent);
number_of_sent_bytes = sendto(m_socket_fd, src_buffer, src_buffer_size, flags,
dest_addr, dest_addr_size);
if (-1 == number_of_sent_bytes) {
if ((EWOULDBLOCK == errno) || (EAGAIN == errno)) {
LOGGER__ERROR("Udp send timeout");
return HAILO_TIMEOUT;
} else if (EINTR == errno) {
LOGGER__ERROR("Udp send interrupted!");
return HAILO_INTERRUPTED_BY_SIGNAL;
} else if (EPIPE == errno) {
// When socket is aborted from another thread sendto will return errno EPIPE
LOGGER__INFO("Udp send aborted!");
return HAILO_STREAM_ABORT;
} else {
LOGGER__ERROR("Udp failed to send data, errno:{}.", errno);
return HAILO_ETH_SEND_FAILURE;
}
}
*bytes_sent = (size_t)number_of_sent_bytes;
return HAILO_SUCCESS;
}
hailo_status Socket::recv_from(uint8_t *dest_buffer, size_t dest_buffer_size, int flags,
sockaddr *src_addr, socklen_t src_addr_size, size_t *bytes_received, bool log_timeouts_in_debug)
{
ssize_t number_of_received_bytes = 0;
socklen_t result_src_addr_size = src_addr_size;
/* Validate arguments */
CHECK_ARG_NOT_NULL(dest_buffer);
CHECK_ARG_NOT_NULL(src_addr);
CHECK_ARG_NOT_NULL(bytes_received);
number_of_received_bytes = recvfrom(m_socket_fd, dest_buffer, dest_buffer_size, flags,
src_addr, &result_src_addr_size);
if (-1 == number_of_received_bytes) {
if ((EWOULDBLOCK == errno) || (EAGAIN == errno)) {
if (log_timeouts_in_debug) {
LOGGER__DEBUG("Udp recvfrom failed with timeout");
} else {
LOGGER__ERROR("Udp recvfrom failed with timeout");
}
return HAILO_TIMEOUT;
} else if (EINTR == errno) {
LOGGER__ERROR("Udp recv interrupted!");
return HAILO_INTERRUPTED_BY_SIGNAL;
} else {
LOGGER__ERROR("Udp failed to recv data");
return HAILO_ETH_RECV_FAILURE;
}
}
else if ((0 == number_of_received_bytes) && (0 != dest_buffer_size)) {
LOGGER__INFO("Udp socket was aborted");
return HAILO_STREAM_ABORT;
}
if (result_src_addr_size > src_addr_size) {
LOGGER__ERROR("src_addr size invalid");
return HAILO_ETH_RECV_FAILURE;
}
*bytes_received = (size_t)number_of_received_bytes;
return HAILO_SUCCESS;
}
hailo_status Socket::has_data(sockaddr *src_addr, socklen_t src_addr_size, bool log_timeouts_in_debug)
{
hailo_status status = HAILO_UNINITIALIZED;
static const size_t DEST_BUFFER_SIZE = 1;
std::array<uint8_t, DEST_BUFFER_SIZE> dest_buffer{};
size_t number_of_received_bytes = 0;
status = recv_from(dest_buffer.data(), dest_buffer.size(), 0, src_addr, src_addr_size, &number_of_received_bytes, log_timeouts_in_debug);
if ((status == HAILO_TIMEOUT) && log_timeouts_in_debug) {
LOGGER__DEBUG("recv_from failed with timeout");
return HAILO_TIMEOUT;
} else {
CHECK_SUCCESS(status);
assert(number_of_received_bytes > 0);
}
return HAILO_SUCCESS;
}
} /* namespace hailort */