Browse Source

[3880] CommandSocketFactory implemented.

Tomek Mrugalski 10 years ago
parent
commit
16ccf1aefe

+ 7 - 6
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&

+ 3 - 11
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();

+ 72 - 2
src/lib/config/command_socket_factory.cc

@@ -13,18 +13,88 @@
 // 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.");
+    }
 }
 
 };

+ 16 - 1
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);
 };
 
 

+ 1 - 0
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
 

+ 69 - 0
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 <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));
+}