23#include <boost/enable_shared_from_this.hpp>
33namespace ph = std::placeholders;
38const size_t BUF_SIZE = 32768;
46class Connection :
public boost::enable_shared_from_this<Connection> {
67 const boost::shared_ptr<UnixDomainSocket>& socket,
68 ConnectionPool& connection_pool,
70 : socket_(socket), timeout_timer_(*io_service), timeout_(timeout),
71 buf_(), response_(), connection_pool_(connection_pool), feed_(),
72 response_in_progress_(false), watch_socket_(new util::WatchSocket()) {
75 .arg(socket_->getNative());
93 timeout_timer_.cancel();
97 void scheduleTimer() {
98 timeout_timer_.setup(std::bind(&Connection::timeoutHandler,
this),
99 timeout_, IntervalTimer::ONE_SHOT);
109 if (!response_in_progress_) {
111 .arg(socket_->getNative());
117 std::string watch_error;
118 if (!watch_socket_->closeSocket(watch_error)) {
124 timeout_timer_.cancel();
140 socket_->asyncReceive(&buf_[0],
sizeof(buf_),
141 std::bind(&Connection::receiveHandler,
142 shared_from_this(), ph::_1, ph::_2));
153 size_t chunk_size = (response_.size() < BUF_SIZE) ? response_.size() : BUF_SIZE;
154 socket_->asyncSend(&response_[0], chunk_size,
155 std::bind(&Connection::sendHandler, shared_from_this(), ph::_1, ph::_2));
161 watch_socket_->markReady();
163 }
catch (
const std::exception& ex) {
181 void receiveHandler(
const boost::system::error_code& ec,
182 size_t bytes_transferred);
193 void sendHandler(
const boost::system::error_code& ec,
194 size_t bytes_transferred);
200 void timeoutHandler();
205 boost::shared_ptr<UnixDomainSocket> socket_;
214 std::array<char, BUF_SIZE> buf_;
217 std::string response_;
220 ConnectionPool& connection_pool_;
228 bool response_in_progress_;
236typedef boost::shared_ptr<Connection> ConnectionPtr;
239class ConnectionPool {
245 void start(
const ConnectionPtr& connection) {
246 connection->doReceive();
247 connections_.insert(connection);
253 void stop(
const ConnectionPtr& connection) {
256 connections_.erase(connection);
257 }
catch (
const std::exception& ex) {
265 for (
auto conn = connections_.begin(); conn != connections_.end();
269 connections_.clear();
275 std::set<ConnectionPtr> connections_;
280Connection::terminate() {
284 }
catch (
const std::exception& ex) {
291Connection::receiveHandler(
const boost::system::error_code& ec,
292 size_t bytes_transferred) {
294 if (ec.value() == boost::asio::error::eof) {
295 std::stringstream os;
296 if (feed_.getProcessedText().empty()) {
297 os <<
"no input data to discard";
300 os <<
"discarding partial command of "
301 << feed_.getProcessedText().size() <<
" bytes";
307 .arg(socket_->getNative()).arg(os.str());
308 }
else if (ec.value() != boost::asio::error::operation_aborted) {
310 .arg(ec.value()).arg(socket_->getNative());
313 connection_pool_.stop(shared_from_this());
316 }
else if (bytes_transferred == 0) {
318 connection_pool_.stop(shared_from_this());
323 .arg(bytes_transferred).arg(socket_->getNative());
334 feed_.postBuffer(&buf_[0], bytes_transferred);
337 if (feed_.needData()) {
343 if (feed_.feedOk()) {
344 cmd = feed_.toElement();
345 response_in_progress_ =
true;
349 timeout_timer_.cancel();
352 rsp = CommandMgr::instance().processCommand(cmd);
354 response_in_progress_ =
false;
371 .arg(cmd ? cmd->str() :
"unknown");
373 "internal server error: no response generated");
383 response_ = rsp->str();
390 connection_pool_.stop(shared_from_this());
394Connection::sendHandler(
const boost::system::error_code& ec,
395 size_t bytes_transferred) {
399 watch_socket_->clearReady();
401 }
catch (
const std::exception& ex) {
408 if (ec.value() != boost::asio::error::operation_aborted) {
410 .arg(socket_->getNative()).arg(ec.message());
421 response_.erase(0, bytes_transferred);
424 .arg(bytes_transferred).arg(response_.size())
425 .arg(socket_->getNative());
428 if (!response_.empty()) {
439 connection_pool_.stop(shared_from_this());
443Connection::timeoutHandler() {
445 .arg(socket_->getNative());
450 }
catch (
const std::exception& ex) {
452 .arg(socket_->getNative())
456 std::stringstream os;
457 os <<
"Connection over control channel timed out";
458 if (!feed_.getProcessedText().empty()) {
459 os <<
", discarded partial command of "
460 << feed_.getProcessedText().size() <<
" bytes";
464 response_ = rsp->str();
535 if (type->stringValue() !=
"unix") {
537 << type->stringValue());
547 if (name->getType() != Element::string) {
555 int lock_fd = open(lock_name.c_str(), O_RDONLY | O_CREAT, 0600);
557 std::string errmsg = strerror(errno);
559 << lock_name <<
", : " << errmsg);
564 int ret = flock(lock_fd, LOCK_EX | LOCK_NB);
566 std::string errmsg = strerror(errno);
568 << lock_name <<
", : " << errmsg);
590 }
catch (
const std::exception& ex) {
599 acceptor_->asyncAccept(*
socket_, [
this](
const boost::system::error_code& ec) {
607 }
else if (ec.value() != boost::asio::error::operation_aborted) {
609 .arg(
acceptor_->getNative()).arg(ec.message());
613 if (ec.value() != boost::asio::error::operation_aborted) {
619CommandMgr::CommandMgr()
625 impl_->openCommandSocket(socket_info);
630 if (impl_->acceptor_ && impl_->acceptor_->isOpen()) {
632 impl_->acceptor_->close();
633 static_cast<void>(::remove(impl_->socket_name_.c_str()));
634 static_cast<void>(::remove(impl_->getLockName().c_str()));
641 impl_->connection_pool_.stopAll();
646 return (impl_->acceptor_ ? impl_->acceptor_->getNative() : -1);
658 impl_->io_service_ = io_service;
663 impl_->timeout_ = timeout;
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
The IntervalTimer class is a wrapper for the ASIO boost::asio::deadline_timer class.
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.
Implementation of the CommandMgr.
void openCommandSocket(const isc::data::ConstElementPtr &socket_info)
Opens acceptor service allowing the control clients to connect.
std::string getLockName()
Returns the lock file name.
boost::shared_ptr< UnixDomainSocket > socket_
Pointer to the socket into which the new connection is accepted.
boost::shared_ptr< UnixDomainSocketAcceptor > acceptor_
Pointer to the acceptor service.
IOServicePtr io_service_
Pointer to the IO service used by the server process for running asynchronous tasks.
long timeout_
Connection timeout.
void doAccept()
Asynchronously accepts next connection.
std::string socket_name_
Path to the unix domain socket descriptor.
CommandMgrImpl()
Constructor.
ConnectionPool connection_pool_
Pool of connections.
Commands Manager implementation for the Kea servers.
int getControlSocketFD()
Returns control socket descriptor.
void closeCommandSocket()
Shuts down any open control sockets.
static CommandMgr & instance()
CommandMgr is a singleton class.
void setIOService(const asiolink::IOServicePtr &io_service)
Sets IO service to be used by the command manager.
void setConnectionTimeout(const long timeout)
Override default connection timeout.
void openCommandSocket(const isc::data::ConstElementPtr &socket_info)
Opens control socket with parameters specified in socket_info.
Command Manager which can delegate commands to a hook library.
State model for asynchronous read of data in JSON format.
An exception indicating a problem with socket operation.
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.
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
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
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
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.
ConstElementPtr createAnswer(const int status_code, const std::string &text, const ConstElementPtr &arg)
const isc::log::MessageID COMMAND_SOCKET_ACCEPT_FAIL
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
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.
Defines the class, WatchSocket.