/*
* Copyright © 2013-2014 Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Authored by: Kevin DuBois
*/
#include "anbox/network/base_socket_messenger.h"
#include "anbox/common/variable_length_array.h"
#include "anbox/logger.h"
#include
#include
#include
#include
namespace bs = boost::system;
namespace ba = boost::asio;
namespace {
/// Buffers need to be big enough to support messages
unsigned int const serialization_buffer_size = 2048;
}
namespace anbox {
namespace network {
template
BaseSocketMessenger::BaseSocketMessenger() {}
template
BaseSocketMessenger::BaseSocketMessenger(
std::shared_ptr> const& socket) {
setup(socket);
}
template
BaseSocketMessenger::~BaseSocketMessenger() {}
template
void BaseSocketMessenger::setup(
std::shared_ptr> const& s) {
socket = s;
socket_fd = anbox::Fd{IntOwnedFd{socket->native_handle()}};
socket->non_blocking(true);
boost::asio::socket_base::send_buffer_size option(64 * 1024);
socket->set_option(option);
}
template
Credentials BaseSocketMessenger::creds() const {
struct ucred cr;
socklen_t cl = sizeof(cr);
auto status = getsockopt(socket_fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl);
if (status)
BOOST_THROW_EXCEPTION(
std::runtime_error("Failed to query client socket credentials"));
return {cr.pid, cr.uid, cr.gid};
}
template
ssize_t BaseSocketMessenger::send_raw(char const* data,
size_t length) {
VariableLengthArray whole_message{length};
std::copy(data, data + length, whole_message.data());
std::unique_lock lg(message_lock);
return ::send(socket_fd, data, length, MSG_NOSIGNAL);
}
template
void BaseSocketMessenger::send(char const* data,
size_t length) {
VariableLengthArray whole_message{length};
std::copy(data, data + length, whole_message.data());
for (;;) {
try {
std::unique_lock lg(message_lock);
ba::write(*socket, ba::buffer(whole_message.data(), whole_message.size()),
boost::asio::transfer_all());
} catch (const boost::system::system_error& err) {
if (err.code() == boost::asio::error::try_again) continue;
throw;
}
break;
}
}
template
void BaseSocketMessenger::async_receive_msg(
AnboxReadHandler const& handler, ba::mutable_buffers_1 const& buffer) {
socket->async_read_some(buffer, handler);
}
template
bs::error_code BaseSocketMessenger::receive_msg(
ba::mutable_buffers_1 const& buffer) {
bs::error_code e;
size_t nread = 0;
while (nread < ba::buffer_size(buffer)) {
nread +=
boost::asio::read(*socket, ba::mutable_buffers_1{buffer + nread}, e);
if (e && e != ba::error::would_block) break;
}
return e;
}
template
size_t BaseSocketMessenger::available_bytes() {
boost::asio::socket_base::bytes_readable command{true};
socket->io_control(command);
return command.get();
}
template
unsigned short BaseSocketMessenger::local_port() const {
return 0;
}
template
void BaseSocketMessenger::set_no_delay() {
const auto fd = socket->native_handle();
int flag = 1;
const auto ret =
::setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
reinterpret_cast(&flag), sizeof(flag));
if (ret < 0) WARNING("Failed to disable TCP delay for socket");
}
template
void BaseSocketMessenger::close() {
socket->close();
}
template class BaseSocketMessenger;
template class BaseSocketMessenger;
} // namespace network
} // namespace anbox