/* * 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