From: Tomek Mrugalski Date: Tue, 26 May 2015 19:26:25 +0000 (+0200) Subject: [3880] CommandSocketFactory implemented. X-Git-Tag: trac3830_base~5^2~19 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=16ccf1aefe6508763e87baf074a811e6a0ffc91d;p=thirdparty%2Fkea.git [3880] CommandSocketFactory implemented. --- diff --git a/src/lib/config/command_mgr.cc b/src/lib/config/command_mgr.cc index 29e1d5b400..814aa56778 100644 --- a/src/lib/config/command_mgr.cc +++ b/src/lib/config/command_mgr.cc @@ -25,9 +25,9 @@ CommandMgr::CommandMgr() { boost::bind(&CommandMgr::listCommandsHandler, this, _1, _2)); } -void CommandMgr::configureCtrlSocket(const isc::data::ConstElementPtr& socket_info) { +int CommandMgr::openCtrlSocket(const isc::data::ConstElementPtr& socket_info) { if (socket_info_) { - isc_throw(CommandSocketError, "There is already a control socket open"); + isc_throw(SocketError, "There is already a control socket open"); } socket_ = CommandSocketFactory::create(socket_info); @@ -38,10 +38,11 @@ void CommandMgr::configureCtrlSocket(const isc::data::ConstElementPtr& socket_in } void CommandMgr::closeCtrlSocket() { - CommandSocketFactory::close(socket_, socket_info_); - - socket_ = 0; - socket_info_.reset(); + if (socket_info_) { + CommandSocketFactory::close(socket_, socket_info_); + socket_ = 0; + socket_info_.reset(); + } } CommandMgr& diff --git a/src/lib/config/command_mgr.h b/src/lib/config/command_mgr.h index 189622b7a3..27b59d86c8 100644 --- a/src/lib/config/command_mgr.h +++ b/src/lib/config/command_mgr.h @@ -39,15 +39,6 @@ public: isc::Exception(file, line, what) { }; }; -/// @brief An exception indicating that operation on the command socket failed -class CommandSocketError : public Exception { -public: - CommandSocketError(const char* file, size_t line, const char* what) : - isc::Exception(file, line, what) { }; -}; - - - /// @brief Commands Manager, responsible for processing external commands /// /// Commands Manager is a generic interface for handling external commands. @@ -96,7 +87,7 @@ public: /// @return the only existing instance of the manager static CommandMgr& instance(); - /// @brief Configured control socket with paramters specified in socket_info + /// @brief Opens control socket with paramters specified in socket_info /// /// Currently supported types are: /// - unix (required parameters: socket-type: unix, socket-name:/unix/path) @@ -104,7 +95,8 @@ public: /// @throw CommandSocketError if socket creation fails /// /// @param socket_info describes control socket parameters - void configureCtrlSocket(const isc::data::ConstElementPtr& socket_info); + /// @return socket descriptor of the socket created + int openCtrlSocket(const isc::data::ConstElementPtr& socket_info); /// @brief Shuts down any open control sockets void closeCtrlSocket(); diff --git a/src/lib/config/command_socket_factory.cc b/src/lib/config/command_socket_factory.cc index 4a48c97d0d..3c1af33b76 100644 --- a/src/lib/config/command_socket_factory.cc +++ b/src/lib/config/command_socket_factory.cc @@ -13,18 +13,88 @@ // PERFORMANCE OF THIS SOFTWARE. #include +#include +#include +#include + +int createUnixSocket(const std::string& file_name) { + + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) { + isc_throw(isc::config::SocketError, "Failed to create AF_UNIX socket."); + } + + /// @todo: Do we need any setsockopt here? + + struct sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, file_name.c_str(), sizeof(addr.sun_path)-1); + if (bind(fd, (struct sockaddr*)&addr, sizeof(addr))) { + isc_throw(isc::config::SocketError, "Failed to bind socket " << fd + << " to " << file_name); + } + + return (fd); +} + +void closeUnixSocket(int socket_fd, const std::string& file_name) { + close(socket_fd); + unlink(file_name.c_str()); +} + +using namespace isc::data; namespace isc { namespace config { int CommandSocketFactory::create(const isc::data::ConstElementPtr& socket_info) { + if(!socket_info) { + isc_throw(BadSocketInfo, "Missing socket_info parameters, can't create socket."); + } + + ConstElementPtr type = socket_info->get("socket-type"); + if (!type) { + isc_throw(BadSocketInfo, "Mandatory 'socket-type' parameter missing"); + } + + ConstElementPtr name = socket_info->get("socket-name"); + if (!name) { + isc_throw(BadSocketInfo, "Mandatory 'socket-name' parameter missing"); + } + if (type->stringValue() == "unix") { + return (createUnixSocket(name->stringValue())); + } else { + isc_throw(BadSocketInfo, "Specified socket type ('" + type->stringValue() + + "') is not supported."); + } } -int CommandSocketFactory::close(int socket_fd, - const isc::data::ConstElementPtr& socket_info) { +void CommandSocketFactory::close(int socket_fd, + const isc::data::ConstElementPtr& socket_info) { + + if(!socket_info) { + isc_throw(BadSocketInfo, "Missing socket_info parameters, can't create socket."); + } + + ConstElementPtr type = socket_info->get("socket-type"); + if (!type) { + isc_throw(BadSocketInfo, "Mandatory 'socket-type' parameter missing"); + } + + ConstElementPtr name = socket_info->get("socket-name"); + if (!name) { + isc_throw(BadSocketInfo, "Mandatory 'socket-name' parameter missing"); + } + if (type->stringValue() == "unix") { + return (closeUnixSocket(socket_fd, name->stringValue())); + } else { + isc_throw(BadSocketInfo, "Specified socket type ('" + type->stringValue() + + "') is not supported."); + } } }; diff --git a/src/lib/config/command_socket_factory.h b/src/lib/config/command_socket_factory.h index 3d2ab93ea7..f46287e911 100644 --- a/src/lib/config/command_socket_factory.h +++ b/src/lib/config/command_socket_factory.h @@ -20,6 +20,21 @@ namespace isc { namespace config { +/// @brief An exception indicating that specified socket parameters are invalid +class BadSocketInfo : public Exception { +public: + BadSocketInfo(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief An exception indicating a problem with socket operation +class SocketError : public Exception { +public: + SocketError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + + /// A factory class for opening command socket /// /// This class provides an interface for opening command socket. @@ -46,7 +61,7 @@ public: /// closing. /// @param socket_fd file descriptor of the socket /// @param socket_info structure that was used to open the socket - static int close(int socket_fd, const isc::data::ConstElementPtr& socket_info); + static void close(int socket_fd, const isc::data::ConstElementPtr& socket_info); }; diff --git a/src/lib/config/tests/Makefile.am b/src/lib/config/tests/Makefile.am index 036b15d2d0..5eeab24127 100644 --- a/src/lib/config/tests/Makefile.am +++ b/src/lib/config/tests/Makefile.am @@ -18,6 +18,7 @@ TESTS = if HAVE_GTEST TESTS += run_unittests run_unittests_SOURCES = module_spec_unittests.cc +run_unittests_SOURCES += command_socket_factory_unittests.cc run_unittests_SOURCES += config_data_unittests.cc run_unittests.cc run_unittests_SOURCES += command_mgr_unittests.cc diff --git a/src/lib/config/tests/command_socket_factory_unittests.cc b/src/lib/config/tests/command_socket_factory_unittests.cc new file mode 100644 index 0000000000..236add6643 --- /dev/null +++ b/src/lib/config/tests/command_socket_factory_unittests.cc @@ -0,0 +1,69 @@ +// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include + +#include +#include +#include + +using namespace isc::config; +using namespace isc::data; + +// Test class for Command Manager +class CommandSocketFactoryTest : public ::testing::Test { +public: + + /// Default constructor + CommandSocketFactoryTest() { + unlink(SOCKET_NAME); + } + + /// Default destructor + ~CommandSocketFactoryTest() { + unlink(SOCKET_NAME); + } + + static const char* SOCKET_NAME; +}; + +const char* CommandSocketFactoryTest::SOCKET_NAME = "test-socket"; + +TEST_F(CommandSocketFactoryTest, unixCreate) { + // Null pointer is obviously a bad idea. + EXPECT_THROW(CommandSocketFactory::create(ConstElementPtr()), + isc::config::BadSocketInfo); + + // So is passing no parameters. + ElementPtr socket_info = Element::createMap(); + EXPECT_THROW(CommandSocketFactory::create(socket_info), + isc::config::BadSocketInfo); + + // We don't support ipx sockets + socket_info->set("socket-type", Element::create("ipx")); + EXPECT_THROW(CommandSocketFactory::create(socket_info), + isc::config::BadSocketInfo); + + socket_info->set("socket-type", Element::create("unix")); + EXPECT_THROW(CommandSocketFactory::create(socket_info), + isc::config::BadSocketInfo); + + socket_info->set("socket-name", Element::create(SOCKET_NAME)); + int fd; + EXPECT_NO_THROW(fd = CommandSocketFactory::create(socket_info)); + EXPECT_NE(-1, fd); + + // It should be possible to close the socket. + EXPECT_NO_THROW(CommandSocketFactory::close(fd, socket_info)); +}