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);
}
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&
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.
/// @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)
/// @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();
// PERFORMANCE OF THIS SOFTWARE.
#include <config/command_socket_factory.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <string.h>
+
+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.");
+ }
}
};
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.
/// 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);
};
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
--- /dev/null
+// 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 <gtest/gtest.h>
+
+#include <cc/data.h>
+#include <config/command_mgr.h>
+#include <config/command_socket_factory.h>
+
+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));
+}