23 #include <boost/bind.hpp> 24 #include <boost/enable_shared_from_this.hpp> 36 const size_t BUF_SIZE = 32768;
44 class Connection :
public boost::enable_shared_from_this<Connection> {
65 const boost::shared_ptr<UnixDomainSocket>& socket,
66 ConnectionPool& connection_pool,
68 : socket_(socket), timeout_timer_(*io_service), timeout_(timeout),
69 buf_(), response_(), connection_pool_(connection_pool), feed_(),
70 response_in_progress_(false), watch_socket_(new util::WatchSocket()) {
73 .arg(socket_->getNative());
91 timeout_timer_.cancel();
95 void scheduleTimer() {
96 timeout_timer_.setup(boost::bind(&Connection::timeoutHandler,
this),
107 if (!response_in_progress_) {
109 .arg(socket_->getNative());
115 std::string watch_error;
116 if (!watch_socket_->closeSocket(watch_error)) {
122 timeout_timer_.cancel();
138 socket_->asyncReceive(&buf_[0],
sizeof(buf_),
139 boost::bind(&Connection::receiveHandler,
140 shared_from_this(), _1, _2));
151 size_t chunk_size = (response_.size() < BUF_SIZE) ? response_.size() : BUF_SIZE;
152 socket_->asyncSend(&response_[0], chunk_size,
153 boost::bind(&Connection::sendHandler, shared_from_this(), _1, _2));
159 watch_socket_->markReady();
161 }
catch (
const std::exception& ex) {
179 void receiveHandler(
const boost::system::error_code& ec,
180 size_t bytes_transferred);
191 void sendHandler(
const boost::system::error_code& ec,
192 size_t bytes_transferred);
198 void timeoutHandler();
203 boost::shared_ptr<UnixDomainSocket> socket_;
212 std::array<char, BUF_SIZE> buf_;
215 std::string response_;
218 ConnectionPool& connection_pool_;
226 bool response_in_progress_;
234 typedef boost::shared_ptr<Connection> ConnectionPtr;
237 class ConnectionPool {
243 void start(
const ConnectionPtr& connection) {
244 connection->doReceive();
245 connections_.insert(connection);
251 void stop(
const ConnectionPtr& connection) {
254 connections_.erase(connection);
255 }
catch (
const std::exception& ex) {
263 for (
auto conn = connections_.begin(); conn != connections_.end();
267 connections_.clear();
273 std::set<ConnectionPtr> connections_;
278 Connection::terminate() {
282 }
catch (
const std::exception& ex) {
289 Connection::receiveHandler(
const boost::system::error_code& ec,
290 size_t bytes_transferred) {
292 if (ec.value() == boost::asio::error::eof) {
293 std::stringstream os;
294 if (feed_.getProcessedText().empty()) {
295 os <<
"no input data to discard";
298 os <<
"discarding partial command of " 299 << feed_.getProcessedText().size() <<
" bytes";
305 .arg(socket_->getNative()).arg(os.str());
306 }
else if (ec.value() != boost::asio::error::operation_aborted) {
308 .arg(ec.value()).arg(socket_->getNative());
311 connection_pool_.stop(shared_from_this());
314 }
else if (bytes_transferred == 0) {
316 connection_pool_.stop(shared_from_this());
321 .arg(bytes_transferred).arg(socket_->getNative());
331 feed_.postBuffer(&buf_[0], bytes_transferred);
334 if (feed_.needData()) {
340 if (feed_.feedOk()) {
342 response_in_progress_ =
true;
346 timeout_timer_.cancel();
349 rsp = CommandMgr::instance().processCommand(cmd);
351 response_in_progress_ =
false;
369 "internal server error: no response generated");
379 response_ = rsp->str();
386 connection_pool_.stop(shared_from_this());
390 Connection::sendHandler(
const boost::system::error_code& ec,
391 size_t bytes_transferred) {
395 watch_socket_->clearReady();
397 }
catch (
const std::exception& ex) {
404 if (ec.value() != boost::asio::error::operation_aborted) {
406 .arg(socket_->getNative()).arg(ec.message());
417 response_.erase(0, bytes_transferred);
420 .arg(bytes_transferred).arg(response_.size())
421 .arg(socket_->getNative());
424 if (!response_.empty()) {
435 connection_pool_.stop(shared_from_this());
439 Connection::timeoutHandler() {
441 .arg(socket_->getNative());
446 }
catch (
const std::exception& ex) {
448 .arg(socket_->getNative())
452 std::stringstream os;
453 os <<
"Connection over control channel timed out";
454 if (!feed_.getProcessedText().empty()) {
455 os <<
", discarded partial command of " 456 << feed_.getProcessedText().size() <<
" bytes";
460 response_ = rsp->str();
476 : io_service_(), acceptor_(), socket_(), socket_name_(),
514 socket_name_.clear();
526 if (type->stringValue() !=
"unix") {
528 << type->stringValue());
538 if (name->getType() != Element::string) {
542 socket_name_ = name->stringValue();
551 acceptor_->open(endpoint);
552 acceptor_->bind(endpoint);
560 }
catch (
const std::exception& ex) {
566 CommandMgrImpl::doAccept() {
569 acceptor_->asyncAccept(*socket_, [
this](
const boost::system::error_code& ec) {
572 ConnectionPtr connection(
new Connection(io_service_, socket_,
575 connection_pool_.start(connection);
577 }
else if (ec.value() != boost::asio::error::operation_aborted) {
579 .arg(acceptor_->getNative()).arg(ec.message());
583 if (ec.value() != boost::asio::error::operation_aborted) {
589 CommandMgr::CommandMgr()
595 impl_->openCommandSocket(socket_info);
600 if (impl_->acceptor_ && impl_->acceptor_->isOpen()) {
602 impl_->acceptor_->close();
603 static_cast<void>(::remove(impl_->socket_name_.c_str()));
610 impl_->connection_pool_.stopAll();
615 return (impl_->acceptor_ ? impl_->acceptor_->getNative() : -1);
627 impl_->io_service_ = io_service;
632 impl_->timeout_ = timeout;
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
std::string socket_name_
Path to the unix domain socket descriptor.
An exception indicating a problem with socket operation.
ConnectionPool connection_pool_
Pool of connections.
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
ConstElementPtr createAnswer(const int status_code, const std::string &text, const ConstElementPtr &arg)
void addExternalSocket(int socketfd, SocketCallback callback)
Adds external socket and a callback.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
boost::shared_ptr< IOService > IOServicePtr
Defines a smart pointer to an IOService instance.
const int CONTROL_RESULT_ERROR
Status code indicating a general failure.
boost::shared_ptr< WatchSocket > WatchSocketPtr
Defines a smart pointer to an instance of a WatchSocket.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
An exception indicating that specified socket parameters are invalid.
constexpr long TIMEOUT_DHCP_SERVER_RECEIVE_COMMAND
Timeout for the DHCP server to receive command over the unix domain socket.
Defines the class, WatchSocket.
Command Manager which can delegate commands to a hook library.
Implementation of the CommandMgr.
long timeout_
Connection timeout.
boost::shared_ptr< UnixDomainSocketAcceptor > acceptor_
Pointer to the acceptor service.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
CommandMgrImpl()
Constructor.
The IntervalTimer class is a wrapper for the ASIO boost::asio::deadline_timer class.
isc::log::Logger command_logger("commands")
Command processing Logger.
void closeCommandSocket()
Shuts down any open control sockets.
void deleteExternalSocket(int socketfd)
Deletes external socket.
Represents unix domain socket implemented in terms of boost asio.
Endpoint for UnixDomainSocket.
IOServicePtr io_service_
Pointer to the IO service used by the server process for running asynchronous tasks.
boost::shared_ptr< const Element > ConstElementPtr
void setConnectionTimeout(const long timeout)
Override default connection timeout.
void setIOService(const asiolink::IOServicePtr &io_service)
Sets IO service to be used by the command manager.
State model for asynchronous read of data in JSON format.
This is a base class for exceptions thrown from the DNS library module.
void openCommandSocket(const isc::data::ConstElementPtr &socket_info)
Opens control socket with parameters specified in socket_info.
Defines the logger used by the top-level component of kea-dhcp-ddns.
boost::shared_ptr< UnixDomainSocket > socket_
Pointer to the socket into which the new connection is accepted.
Implements acceptor service for UnixDomainSocket.
Commands Manager implementation for the Kea servers.
int getControlSocketFD()
Returns control socket descriptor.
This file contains several functions and constants that are used for handling commands and responses ...
static IfaceMgr & instance()
IfaceMgr is a singleton class.
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
static CommandMgr & instance()
CommandMgr is a singleton class.