blob: a13be584012f2960716030350cd00755f33e51a9 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ignite/network/detail/linux/sockets.h"
#include "ignite/network/socket_client.h"
#include <cerrno>
#include <cstring>
#include <sstream>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/tcp.h>
#include <poll.h>
#include <sys/socket.h>
namespace ignite::network::detail {
#if defined(__linux__)
std::string get_socket_error_message(int error) {
std::stringstream res;
res << "error_code=" << error;
if (error == 0)
return res.str();
char err_buf[1024] = {0};
const char *err_str = strerror_r(error, err_buf, sizeof(err_buf));
if (err_str)
res << ", msg=" << err_str;
return res.str();
}
#elif defined(__APPLE__)
std::string get_socket_error_message(int error) {
std::stringstream res;
res << "error_code=" << error;
if (error == 0)
return res.str();
char err_buf[1024] = {0};
const int err_res = strerror_r(error, err_buf, sizeof(err_buf));
switch (err_res) {
case 0:
res << ", msg=" << err_buf;
break;
case ERANGE:
// Buffer too small.
break;
default:
case EINVAL:
// Invalid error code.
break;
}
return res.str();
}
#endif
std::string get_last_socket_error_message() {
int last_error = errno;
return get_socket_error_message(last_error);
}
void try_set_socket_options(int socket_fd, int buf_size, bool no_delay, bool out_of_band, bool keep_alive) {
setsockopt(socket_fd, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<char *>(&buf_size), sizeof(buf_size));
setsockopt(socket_fd, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<char *>(&buf_size), sizeof(buf_size));
int iNoDelay = no_delay ? 1 : 0;
setsockopt(socket_fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&iNoDelay), sizeof(iNoDelay));
int iOutOfBand = out_of_band ? 1 : 0;
setsockopt(socket_fd, SOL_SOCKET, SO_OOBINLINE, reinterpret_cast<char *>(&iOutOfBand), sizeof(iOutOfBand));
int iKeepAlive = keep_alive ? 1 : 0;
int res =
setsockopt(socket_fd, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast<char *>(&iKeepAlive), sizeof(iKeepAlive));
if (SOCKET_ERROR == res) {
// There is no sense in configuring keep alive params if we failed to set up keep alive mode.
return;
}
// The time in seconds the connection needs to remain idle before starts sending keepalive probes.
enum { KEEP_ALIVE_IDLE_TIME = 60 };
// The time in seconds between individual keepalive probes.
enum { KEEP_ALIVE_PROBES_PERIOD = 1 };
int idle_opt = KEEP_ALIVE_IDLE_TIME;
int idle_retry_opt = KEEP_ALIVE_PROBES_PERIOD;
#ifdef __APPLE__
setsockopt(socket_fd, IPPROTO_TCP, TCP_KEEPALIVE, reinterpret_cast<char *>(&idle_opt), sizeof(idle_opt));
#else
setsockopt(socket_fd, IPPROTO_TCP, TCP_KEEPIDLE, reinterpret_cast<char *>(&idle_opt), sizeof(idle_opt));
#endif
setsockopt(
socket_fd, IPPROTO_TCP, TCP_KEEPINTVL, reinterpret_cast<char *>(&idle_retry_opt), sizeof(idle_retry_opt));
}
int wait_on_socket(int socket, std::int32_t timeout, bool rd) {
int32_t timeout0 = timeout == 0 ? -1 : timeout;
int lastError = 0;
int ret;
do {
struct pollfd fds[1];
fds[0].fd = socket;
fds[0].events = rd ? POLLIN : POLLOUT;
ret = poll(fds, 1, timeout0 * 1000);
if (ret == SOCKET_ERROR)
lastError = errno;
} while (ret == SOCKET_ERROR && lastError == EINTR);
if (ret == SOCKET_ERROR)
return -lastError;
socklen_t size = sizeof(lastError);
int res = getsockopt(socket, SOL_SOCKET, SO_ERROR, reinterpret_cast<char *>(&lastError), &size);
if (res != SOCKET_ERROR && lastError != 0)
return -lastError;
if (ret == 0)
return socket_client::wait_result::TIMEOUT;
return socket_client::wait_result::SUCCESS;
}
bool set_non_blocking_mode(int socket_fd, bool non_blocking) {
int flags = fcntl(socket_fd, F_GETFL, 0);
if (flags == -1)
return false;
bool current_non_blocking = flags & O_NONBLOCK;
if (non_blocking == current_non_blocking)
return true;
flags ^= O_NONBLOCK;
int res = fcntl(socket_fd, F_SETFL, flags);
return res != -1;
}
} // namespace ignite::network::detail