26#include <boost/enable_shared_from_this.hpp>
37namespace ph = std::placeholders;
42const size_t BUF_SIZE = 32768;
49typedef boost::shared_ptr<Connection> ConnectionPtr;
55class Connection :
public boost::enable_shared_from_this<Connection> {
77 const boost::shared_ptr<UnixDomainSocket>& socket,
78 ConnectionPool& connection_pool,
81 : io_service_(io_service), socket_(socket), timeout_timer_(io_service_),
82 timeout_(timeout), buf_(), response_(), position_(0),
83 connection_pool_(connection_pool), feed_(), watch_socket_(),
84 use_external_(use_external), defer_shutdown_(false) {
87 .arg(socket_->getNative());
92 watch_socket_.reset(
new util::WatchSocket());
108 timeout_timer_.cancel();
112 void scheduleTimer() {
113 timeout_timer_.setup(std::bind(&Connection::timeoutHandler,
this),
124 if (defer_shutdown_) {
125 io_service_->post(std::bind([](ConnectionPtr c) { c->stop(); }, shared_from_this()));
130 .arg(socket_->getNative());
137 std::string watch_error;
138 if (!watch_socket_->closeSocket(watch_error)) {
145 timeout_timer_.cancel();
160 socket_->asyncReceive(&buf_[0],
sizeof(buf_),
161 std::bind(&Connection::receiveHandler,
162 shared_from_this(), ph::_1, ph::_2));
175 size_t remaining = response_.size() - position_;
176 socket_->asyncSend(&response_[position_], remaining,
177 std::bind(&Connection::sendHandler, shared_from_this(), ph::_1, ph::_2));
184 watch_socket_->markReady();
185 }
catch (
const std::exception& ex) {
204 void receiveHandler(
const boost::system::error_code& ec,
205 size_t bytes_transferred);
215 void sendHandler(
const boost::system::error_code& ec,
216 size_t bytes_transferred);
222 void timeoutHandler();
230 boost::shared_ptr<UnixDomainSocket> socket_;
233 IntervalTimer timeout_timer_;
239 std::array<char, BUF_SIZE> buf_;
242 std::string response_;
248 ConnectionPool& connection_pool_;
263 bool defer_shutdown_;
267class ConnectionPool {
273 void start(
const ConnectionPtr& connection) {
274 connection->doReceive();
275 connections_.insert(connection);
281 void stop(
const ConnectionPtr& connection) {
284 connections_.erase(connection);
285 }
catch (
const std::exception& ex) {
293 for (
auto const& conn : connections_) {
296 connections_.clear();
302 std::set<ConnectionPtr> connections_;
307Connection::terminate() {
311 }
catch (
const std::exception& ex) {
318Connection::receiveHandler(
const boost::system::error_code& ec,
319 size_t bytes_transferred) {
321 if (ec.value() == boost::asio::error::eof) {
322 std::stringstream os;
323 if (feed_.getProcessedText().empty()) {
324 os <<
"no input data to discard";
326 os <<
"discarding partial command of "
327 << feed_.getProcessedText().size() <<
" bytes";
333 .arg(socket_->getNative()).arg(os.str());
334 }
else if (ec.value() != boost::asio::error::operation_aborted) {
336 .arg(ec.value()).arg(socket_->getNative());
339 connection_pool_.stop(shared_from_this());
342 }
else if (bytes_transferred == 0) {
344 connection_pool_.stop(shared_from_this());
349 .arg(bytes_transferred).arg(socket_->getNative());
360 feed_.postBuffer(&buf_[0], bytes_transferred);
363 if (feed_.needData()) {
369 if (feed_.feedOk()) {
370 cmd = feed_.toElement();
372 defer_shutdown_ =
true;
374 std::unique_ptr<Connection, void (*)(Connection*)> p(
this, [](Connection* c) {
375 c->defer_shutdown_ =
false;
380 timeout_timer_.cancel();
389 isc_throw(BadValue, feed_.getErrorMessage());
392 }
catch (
const Exception& ex) {
398 if (!rsp || rsp->str().empty()) {
400 .arg(cmd ? cmd->str() :
"unknown");
402 "internal server error: no response generated");
412 response_ = rsp->str();
419 connection_pool_.stop(shared_from_this());
423Connection::sendHandler(
const boost::system::error_code& ec,
424 size_t bytes_transferred) {
429 watch_socket_->clearReady();
430 }
catch (
const std::exception& ex) {
438 if (ec.value() != boost::asio::error::operation_aborted) {
440 .arg(socket_->getNative()).arg(ec.message());
450 if (bytes_transferred >= response_.size() - position_) {
451 position_ = response_.size();
453 position_ += bytes_transferred;
457 .arg(bytes_transferred)
458 .arg(response_.size() - position_)
459 .arg(socket_->getNative());
462 if (position_ < response_.size()) {
473 connection_pool_.stop(shared_from_this());
477Connection::timeoutHandler() {
479 .arg(socket_->getNative());
484 }
catch (
const std::exception& ex) {
486 .arg(socket_->getNative())
490 std::stringstream os;
491 os <<
"Connection over control channel timed out";
492 if (!feed_.getProcessedText().empty()) {
493 os <<
", discarded partial command of "
494 << feed_.getProcessedText().size() <<
" bytes";
498 response_ = rsp->str();
581 <<
config->getPosition() <<
")");
584 for (
auto const& socket :
config->listValue()) {
590 if (
data.second->usable_) {
594 data.second->usable_ =
false;
611 auto it =
sockets_.find(cmd_config->getSocketName());
614 it->second->usable_ =
true;
620 std::string lock_name = cmd_config->getLockName();
621 int lock_fd = open(lock_name.c_str(), O_RDONLY | O_CREAT, 0600);
623 std::string errmsg = strerror(errno);
625 << lock_name <<
", : " << errmsg);
630 int ret = flock(lock_fd, LOCK_EX | LOCK_NB);
632 std::string errmsg = strerror(errno);
635 << lock_name <<
", : " << errmsg);
640 static_cast<void>(::remove(cmd_config->getSocketName().c_str()));
643 .arg(cmd_config->getSocketName());
646 socket_info->config_ = cmd_config;
647 socket_info->lock_fd_ = lock_fd;
653 socket_info->acceptor_->open(endpoint);
661 socket_info->acceptor_->bind(endpoint);
663 socket_info->acceptor_->listen();
671 }
catch (
const std::exception& ex) {
673 static_cast<void>(::remove(cmd_config->getLockName().c_str()));
677 sockets_[cmd_config->getSocketName()] = socket_info;
684 if (
info->acceptor_ &&
info->acceptor_->isOpen()) {
688 info->acceptor_->close();
689 static_cast<void>(::remove(
info->config_->getSocketName().c_str()));
690 static_cast<void>(::remove(
info->config_->getLockName().c_str()));
698 if (
info->lock_fd_ != -1) {
699 close(
info->lock_fd_);
702 auto it =
sockets_.find(
info->config_->getSocketName());
730 info->acceptor_->asyncAccept(*
info->socket_,
731 [
this,
info](
const boost::system::error_code& ec) {
734 ConnectionPtr connection(new Connection(io_service_, info->socket_,
738 connection_pool_.start(connection);
740 }
else if (ec.value() != boost::asio::error::operation_aborted) {
741 LOG_ERROR(command_logger, COMMAND_SOCKET_ACCEPT_FAIL)
742 .arg(info->acceptor_->getNative()).arg(ec.message());
746 if (ec.value() != boost::asio::error::operation_aborted) {
756 auto const& it =
sockets_.find(
info->config_->getSocketName());
758 return (it->second->acceptor_->getNative());
761 return (
sockets_.begin()->second->acceptor_->getNative());
771 impl_->openCommandSocket(
config);
776 impl_->openCommandSockets(
config);
781 impl_->closeCommandSocket(
info);
786 impl_->closeCommandSockets();
791 return (impl_->getControlSocketFD(
info));
796 static UnixCommandMgr cmd_mgr;
802 impl_->io_service_ = io_service;
807 impl_->timeout_ = timeout;
812 impl_->use_external_ = use_external;
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Implements acceptor service for UnixDomainSocket.
Endpoint for UnixDomainSocket.
Represents unix domain socket implemented in terms of boost asio.
An exception indicating that specified socket parameters are invalid.
virtual isc::data::ConstElementPtr processCommand(const isc::data::ConstElementPtr &cmd)
Triggers command processing.
static CommandMgr & instance()
CommandMgr is a singleton class.
An exception indicating a problem with socket operation.
UNIX command config aka UNIX control socket info class.
Implementation of the UnixCommandMgr.
void closeCommandSocket(UnixSocketInfoPtr info)
Shuts down any open unix control sockets.
void openCommandSockets(const isc::data::ConstElementPtr config)
Opens acceptor service allowing the control clients to connect.
long timeout_
Connection timeout.
ConnectionPool connection_pool_
Pool of connections.
bool use_external_
Use external sockets flag.
void doAccept(UnixSocketInfoPtr info)
Asynchronously accepts next connection.
void closeCommandSockets(bool remove=true)
Shuts down any open unix control sockets.
std::map< std::string, UnixSocketInfoPtr > sockets_
The UNIX socket data (configuration, acceptor, etc.).
void openCommandSocket(const isc::data::ConstElementPtr config)
Opens acceptor service allowing the control clients to connect.
UnixCommandMgrImpl()
Constructor.
IOServicePtr io_service_
Pointer to the IO service used by the server process for running asynchronous tasks.
int getControlSocketFD(UnixSocketInfoPtr info)
Returns unix control socket descriptor.
Unix Commands Manager implementation for the Kea servers.
static UnixCommandMgr & instance()
UnixCommandMgr is a singleton class.
void setIOService(const asiolink::IOServicePtr &io_service)
Sets IO service to be used by the unix command manager.
int getControlSocketFD(UnixSocketInfoPtr info=UnixSocketInfoPtr())
Returns unix control socket descriptor.
void openCommandSockets(const isc::data::ConstElementPtr config)
Opens unix control socket with parameters specified in socket_info (required parameters: socket-type:...
void closeCommandSockets()
Shuts down any open unix control sockets.
void addExternalSockets(bool use_external=true)
Use external sockets flag.
void closeCommandSocket(UnixSocketInfoPtr info=UnixSocketInfoPtr())
Shuts down any open unix control sockets.
void setConnectionTimeout(const long timeout)
Override default connection timeout.
void openCommandSocket(const isc::data::ConstElementPtr config)
Opens unix control socket with parameters specified in socket_info (required parameters: socket-type:...
To be removed. Please use ConfigError instead.
void deleteExternalSocket(int socketfd)
Deletes external socket.
static IfaceMgr & instance()
IfaceMgr is a singleton class.
void addExternalSocket(int socketfd, SocketCallback callback)
Adds external socket and a callback.
RAII device to relax umask (adding group write for sockets).
This file contains several functions and constants that are used for handling commands and responses ...
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
boost::shared_ptr< IOService > IOServicePtr
Defines a smart pointer to an IOService instance.
const isc::log::MessageID COMMAND_PROCESS_ERROR1
boost::shared_ptr< UnixSocketInfo > UnixSocketInfoPtr
Pointer to a UnixSocketInfo object.
const isc::log::MessageID COMMAND_SOCKET_READ_FAIL
const isc::log::MessageID COMMAND_SOCKET_CONNECTION_SHUTDOWN_FAIL
const isc::log::MessageID COMMAND_SOCKET_CONNECTION_CLOSED
boost::shared_ptr< UnixCommandConfig > UnixCommandConfigPtr
Pointer to a UnixCommandConfig object.
const int CONTROL_RESULT_ERROR
Status code indicating a general failure.
const isc::log::MessageID COMMAND_SOCKET_CONNECTION_CANCEL_FAIL
const isc::log::MessageID COMMAND_SOCKET_CONNECTION_OPENED
const isc::log::MessageID COMMAND_SOCKET_CLOSED_BY_FOREIGN_HOST
const isc::log::MessageID COMMAND_SOCKET_CONNECTION_TIMEOUT
const isc::log::MessageID COMMAND_ACCEPTOR_START
ConstElementPtr createAnswer()
Creates a standard config/command level success answer message (i.e.
const isc::log::MessageID COMMAND_RESPONSE_ERROR
constexpr long TIMEOUT_DHCP_SERVER_RECEIVE_COMMAND
Timeout for the DHCP server to receive command over the unix domain socket.
const isc::log::MessageID COMMAND_SOCKET_WRITE_FAIL
const isc::log::MessageID COMMAND_SOCKET_CONNECTION_CLOSE_FAIL
const isc::log::MessageID COMMAND_WATCH_SOCKET_CLOSE_ERROR
const isc::log::MessageID COMMAND_SOCKET_READ
isc::log::Logger command_logger("commands")
Command processing Logger.
const isc::log::MessageID COMMAND_WATCH_SOCKET_MARK_READY_ERROR
const isc::log::MessageID COMMAND_SOCKET_WRITE
const isc::log::MessageID COMMAND_WATCH_SOCKET_CLEAR_ERROR
ElementPtr copy(ConstElementPtr from, unsigned level)
Copy the data up to a nesting level.
boost::shared_ptr< const Element > ConstElementPtr
boost::shared_ptr< WatchSocket > WatchSocketPtr
Defines a smart pointer to an instance of a WatchSocket.
Defines the logger used by the top-level component of kea-lfc.
Structure used to store UNIX connection data.
Defines the class, WatchSocket.