From: Tomek Mrugalski Date: Tue, 16 Jun 2015 12:47:52 +0000 (+0200) Subject: [3880] Changes after review: X-Git-Tag: trac3830_base~5^2~4 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d622d50755abcc00950520ea7fc8cc06fca05a45;p=thirdparty%2Fkea.git [3880] Changes after review: - CommandSocket, UNIXCommandSocket and ConnectionSocket classes added. - Updated unit-test to use --- diff --git a/src/lib/config/Makefile.am b/src/lib/config/Makefile.am index 49aad25a8a..7d2ba47f81 100644 --- a/src/lib/config/Makefile.am +++ b/src/lib/config/Makefile.am @@ -18,6 +18,7 @@ lib_LTLIBRARIES = libkea-cfgclient.la libkea_cfgclient_la_SOURCES = config_data.h config_data.cc libkea_cfgclient_la_SOURCES += module_spec.h module_spec.cc libkea_cfgclient_la_SOURCES += command_mgr.cc command_mgr.h +libkea_cfgclient_la_SOURCES += command_socket.cc command_socket.h libkea_cfgclient_la_SOURCES += command_socket_factory.cc command_socket_factory.h libkea_cfgclient_la_SOURCES += config_log.h config_log.cc diff --git a/src/lib/config/command_mgr.cc b/src/lib/config/command_mgr.cc index 1a69fb70a9..f359293412 100644 --- a/src/lib/config/command_mgr.cc +++ b/src/lib/config/command_mgr.cc @@ -30,30 +30,52 @@ CommandMgr::CommandMgr() { boost::bind(&CommandMgr::listCommandsHandler, this, _1, _2)); } -int CommandMgr::openCommandSocket(const isc::data::ConstElementPtr& socket_info) { - if (socket_info_) { +CommandSocketPtr +CommandMgr::openCommandSocket(const isc::data::ConstElementPtr& socket_info) { + if (socket_) { isc_throw(SocketError, "There is already a control socket open"); } socket_ = CommandSocketFactory::create(socket_info); - socket_info_ = socket_info; - - // Install this socket in Interface Manager. - isc::dhcp::IfaceMgr::instance().addExternalSocket(socket_, - boost::bind(&isc::config::CommandMgr::connectionAcceptor, socket_)); return (socket_); } void CommandMgr::closeCommandSocket() { - if (socket_info_) { + // First, let's close the socket for incoming new connections. + if (socket_) { + socket_->close(); + socket_.reset(); + } + + // Now let's close all existing connections that we may have. + for (std::list::iterator conn = connections_.begin(); + conn != connections_.end(); ++conn) { + (*conn)->close(); + } + connections_.clear(); +} + - isc::dhcp::IfaceMgr::instance().deleteExternalSocket(socket_); +void CommandMgr::addConnection(const CommandSocketPtr& conn) { + connections_.push_back(conn); +} + +bool CommandMgr::closeConnection(int fd) { + + // Let's iterate over all currently registered connections. + for (std::list::iterator conn = connections_.begin(); + conn != connections_.end(); ++conn) { - CommandSocketFactory::close(socket_, socket_info_); - socket_ = 0; - socket_info_.reset(); + // If found, close it. + if ((*conn)->getFD() == fd) { + (*conn)->close(); + connections_.erase(conn); + return (true); + } } + + return (false); } CommandMgr& @@ -104,35 +126,6 @@ void CommandMgr::deregisterAll() { boost::bind(&CommandMgr::listCommandsHandler, this, _1, _2)); } -void -CommandMgr::connectionAcceptor(int sockfd) { - - /// @todo: Either make this generic or rename this method - /// to CommandSocketFactory::unixConnectionAcceptor - struct sockaddr_un client_addr; - socklen_t client_addr_len; - client_addr_len = sizeof(client_addr); - - // Accept incoming connection. This will create a separate socket for - // handling this specific connection. - int fd2 = accept(sockfd, (struct sockaddr*) &client_addr, &client_addr_len); - - // Not sure if this is really needed, but let's set it to non-blocking mode. - fcntl(fd2, F_SETFL, O_NONBLOCK); - - // Install commandReader callback. When there's any data incoming on this - // socket, commandReader will be called and process it. It may also - // eventually close this socket. - isc::dhcp::IfaceMgr::instance().addExternalSocket(fd2, - boost::bind(&isc::config::CommandMgr::commandReader, fd2)); - - // Remember this socket descriptor. It will be needed when we shut down the - // server. - instance().connections_.push_back(fd2); - - LOG_INFO(command_logger, COMMAND_SOCKET_CONNECTION_OPENED).arg(fd2).arg(sockfd); -} - void CommandMgr::commandReader(int sockfd) { @@ -152,19 +145,8 @@ CommandMgr::commandReader(int sockfd) { return; } else if (rval == 0) { - // read of 0 bytes means end-of-file. In other words the connection is - // being closed. - - LOG_INFO(command_logger, COMMAND_SOCKET_CONNECTION_CLOSED).arg(sockfd); - - // Unregister this callback - isc::dhcp::IfaceMgr::instance().deleteExternalSocket(sockfd); - - // Close the socket. - close(sockfd); - // Remove it from the active connections list. - instance().connections_.remove(sockfd); + instance().closeConnection(sockfd); return; } diff --git a/src/lib/config/command_mgr.h b/src/lib/config/command_mgr.h index 5b20959100..83c98958b1 100644 --- a/src/lib/config/command_mgr.h +++ b/src/lib/config/command_mgr.h @@ -16,6 +16,7 @@ #define COMMAND_MGR_H #include +#include #include #include #include @@ -98,8 +99,9 @@ public: /// @throw SocketError if command socket is already open. /// /// @param socket_info describes control socket parameters - /// @return socket descriptor of the socket created - int openCommandSocket(const isc::data::ConstElementPtr& socket_info); + /// @return object representing a socket + CommandSocketPtr + openCommandSocket(const isc::data::ConstElementPtr& socket_info); /// @brief Shuts down any open control sockets void closeCommandSocket(); @@ -124,16 +126,6 @@ public: /// it may be called by external code explicitly. Hence this method is public. isc::data::ConstElementPtr processCommand(const isc::data::ConstElementPtr& cmd); - /// @brief Callback used to accept incoming connections. - /// - /// This callback is used on a control socket. Once called, it will accept - /// incoming connection, create new socket for it and install @ref commandReader - /// for that new socket in @ref isc::dhcp::IfaceMgr. - /// - /// @param sockfd socket descriptor of a socket capable of accepting - /// incoming connections - static void connectionAcceptor(int sockfd); - /// @brief Reads data from a socket, parses as JSON command and processes it /// /// This method is used to handle traffic on connected socket. This callback @@ -150,12 +142,22 @@ public: /// handled at all times. void deregisterAll(); + /// @brief Adds an information about opened connection socket + /// + /// @param conn Connection socket to be stored + void addConnection(const CommandSocketPtr& conn); + + /// @brief Closes connection with a specific socket descriptor + /// + /// @param fd socket descriptor + /// @return true if closed successfully, false if not found + bool closeConnection(int fd); /// @brief Returns control socket descriptor /// /// This method should be used only in tests. int getControlSocketFD() const { - return (socket_); + return (socket_->getFD()); } private: @@ -181,14 +183,17 @@ private: /// @brief Container for command handlers HandlerContainer handlers_; - /// @brief Socket file descriptor - int socket_; - - /// @brief Parameters for control socket - isc::data::ConstElementPtr socket_info_; + /// @brief Control socket structure + /// + /// This is the socket that accepts incoming connections. There can be at + /// most one (if command channel is configured). + CommandSocketPtr socket_; - /// @brief Socket descriptors for open connections - std::list connections_; + /// @brief Sockets for open connections + /// + /// These are the sockets that are dedicated to handle a specific connection. + /// Their number is equal to number of current control connections. + std::list connections_; }; }; // end of isc::config namespace diff --git a/src/lib/config/command_socket.cc b/src/lib/config/command_socket.cc new file mode 100644 index 0000000000..a27cb54874 --- /dev/null +++ b/src/lib/config/command_socket.cc @@ -0,0 +1,52 @@ +// 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 +#include +#include + +namespace isc { +namespace config { + +ConnectionSocket::ConnectionSocket(int sockfd) + :CommandSocket(isc::data::ConstElementPtr()) { + sockfd_ = sockfd; + + // Install commandReader callback. When there's any data incoming on this + // socket, commandReader will be called and process it. It may also + // eventually close this socket. + isc::dhcp::IfaceMgr::instance().addExternalSocket(sockfd, + boost::bind(&ConnectionSocket::receiveHandler, this)); + } + +void ConnectionSocket::close() { + LOG_INFO(command_logger, COMMAND_SOCKET_CONNECTION_CLOSED).arg(sockfd_); + + // Unregister this callback + isc::dhcp::IfaceMgr::instance().deleteExternalSocket(sockfd_); + + // We're closing a connection, not the whole socket. It's ok to just + // close the connection and don't delete anything. + ::close(sockfd_); +} + +void ConnectionSocket::receiveHandler() { + CommandMgr::instance().commandReader(sockfd_); +} + +}; +}; diff --git a/src/lib/config/command_socket.h b/src/lib/config/command_socket.h new file mode 100644 index 0000000000..018f6715c8 --- /dev/null +++ b/src/lib/config/command_socket.h @@ -0,0 +1,123 @@ +// 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. + +#ifndef COMMAND_SOCKET_H +#define COMMAND_SOCKET_H + +#include +#include + +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) { }; +}; + +/// @brief Abstract base class that represents an open command socket +/// +/// This class is not expected to be instantiated directly. Derived classes +/// are expected to handle specific socket types (e.g. UNIX or https). +/// +/// For derived classes, see @ref UnixCommandSocket for a socket that +/// accepts connections over UNIX socket and @ref ConnectionSocket that +/// handles established connections (currently over UNIX sockets, but +/// should be generic). +class CommandSocket { +public: + /// @brief Default constructor + /// + /// @param socket_info socket information from the config + CommandSocket(const isc::data::ConstElementPtr& socket_info) + :socket_info_(socket_info) { + } + + /// @brief Method used to handle incoming data + /// + /// This may be registered in @ref isc::dhcp::IfaceMgr + virtual void receiveHandler() = 0; + + /// @brief General method for closing socket. + /// + /// This is the default implementation that simply closes + /// the socket. Derived classes may do additional steps + /// to terminate the connection. + virtual void close() { + ::close(sockfd_); + } + + /// @brief Virtual destructor. + virtual ~CommandSocket() { + close(); + } + + /// @brief Returns socket descriptor. + int getFD() const { + return (sockfd_); + } + +protected: + /// Stores socket descriptor. + int sockfd_; + + /// Stores socket information. + isc::data::ConstElementPtr socket_info_; +}; + +/// Pointer to a command socket object +typedef boost::shared_ptr CommandSocketPtr; + +/// @brief This class represents a straming socket for handling connections +/// +/// Initially a socket (e.g. UNIX) is opened (represented by other classes, e.g. +/// @ref UnixCommandSocket). Once incoming connection is detected, that class +/// calls accept(), which returns a new socket dedicated to handling that +/// specific connection. That socket is represented by this class. +class ConnectionSocket : public CommandSocket { +public: + /// @brief Default constructor + /// + /// This constructor is used in methods that call accept on existing + /// sockets. accept() returns a socket descriptor. Hence only one + /// parameter here. + /// + /// @param sockfd socket descriptor + ConnectionSocket(int sockfd); + + /// @brief Method used to handle incoming data + /// + /// This method calls isc::config::CommandMgr::commandReader method. + virtual void receiveHandler(); + + /// @brief Closes socket. + /// + /// This method closes the socket, prints appropriate log message and + /// unregisters callback from @ref isc::dhcp::IfaceMgr. + virtual void close(); +}; + +}; +}; + +#endif diff --git a/src/lib/config/command_socket_factory.cc b/src/lib/config/command_socket_factory.cc index d072def82a..588cce6e34 100644 --- a/src/lib/config/command_socket_factory.cc +++ b/src/lib/config/command_socket_factory.cc @@ -14,98 +14,154 @@ #include #include -#include +#include +#include +#include #include #include #include -#include +#include #include -using namespace isc::config; - -namespace { - -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."); - } - - // Let's remove the old file. We don't care about any possible - // errors here. The file should not be there if the file was - // shut down properly. - remove(file_name.c_str()); - - // Set this socket to be non-blocking one. - fcntl(fd, F_SETFL, O_NONBLOCK); - - // Now bind the socket to the specified path. - 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); - } - - // One means that we allow at most 1 awaiting connections. - // Any additional attempts will get ECONNREFUSED error. - // That means that at any given time, there may be at most one controlling - // connection. - /// @todo: Make this a configurable. - int status = listen(fd, 1); - if (status < 0) { - isc_throw(isc::config::SocketError, "Failed to listen on socket fd=" - << fd << ", filename=" << file_name); - } - - LOG_INFO(command_logger, COMMAND_SOCKET_UNIX_OPEN).arg(fd).arg(file_name); - - return (fd); -} - -void closeUnixSocket(int socket_fd, const std::string& file_name) { - LOG_INFO(command_logger, COMMAND_SOCKET_UNIX_CLOSE).arg(socket_fd).arg(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."); +/// @brief Wrapper for UNIX stream sockets +/// +/// There are two UNIX socket types: datagram-based (equivalent of UDP) and +/// stream-based (equivalent of TCP). This class represents stream-based +/// sockets. It opens up a unix-socket and waits for incoming connections. +/// Once incoming connection is detected, accept() system call is called +/// and a new socket for that particular connection is returned. A new +/// object of @ref ConnectionSocket is created. +class UnixCommandSocket : public CommandSocket { +public: + /// @brief Default constructor + /// + /// This socket_info map is expected to have at least one parameter: + /// 'socket-name' that specifies UNIX socket path. It's typically + /// a file somewhere in /tmp or /var/run/kea directory. + /// + /// @param socket_info socket description from the configuration + UnixCommandSocket(const isc::data::ConstElementPtr& socket_info) + : CommandSocket(socket_info) { + + ConstElementPtr name = socket_info->get("socket-name"); + if (!name) { + isc_throw(BadSocketInfo, "Mandatory 'socket-name' parameter missing"); + } + filename_ = name->stringValue(); + + sockfd_ = createUnixSocket(filename_); + + // Install this socket in Interface Manager. + isc::dhcp::IfaceMgr::instance().addExternalSocket(sockfd_, + boost::bind(&UnixCommandSocket::receiveHandler, this)); } - ConstElementPtr type = socket_info->get("socket-type"); - if (!type) { - isc_throw(BadSocketInfo, "Mandatory 'socket-type' parameter missing"); +private: + + /// @brief Auxiliary method for creating a UNIX socket + /// + /// @param file_name specifies socket file path + 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."); + } + + // Let's remove the old file. We don't care about any possible + // errors here. The file should not be there if the file was + // shut down properly. + remove(file_name.c_str()); + + // Set this socket to be non-blocking one. + fcntl(fd, F_SETFL, O_NONBLOCK); + + // Now bind the socket to the specified path. + 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))) { + ::close(fd); + remove(file_name.c_str()); + isc_throw(isc::config::SocketError, "Failed to bind socket " << fd + << " to " << file_name); + } + + // One means that we allow at most 1 awaiting connections. + // Any additional attempts will get ECONNREFUSED error. + // That means that at any given time, there may be at most one controlling + // connection. + /// @todo: Make the number of parallel connections configurable. + int status = listen(fd, 1); + if (status < 0) { + ::close(fd); + remove(file_name.c_str()); + isc_throw(isc::config::SocketError, "Failed to listen on socket fd=" + << fd << ", filename=" << file_name); + } + + // Woohoo! Socket opened, let's log it! + LOG_INFO(command_logger, COMMAND_SOCKET_UNIX_OPEN).arg(fd).arg(file_name); + + return (fd); } - ConstElementPtr name = socket_info->get("socket-name"); - if (!name) { - isc_throw(BadSocketInfo, "Mandatory 'socket-name' parameter missing"); + /// CommandMgr::connectionAcceptor(int sockfd) { + /// @brief Callback used to accept incoming connections. + /// + /// This callback is used on a control socket. Once called, it will accept + /// incoming connection, create new socket for it and install + /// @ref commandReader for that new socket in @ref isc::dhcp::IfaceMgr. + void receiveHandler() { + + // This method is specific to receiving data over UNIX socket, so using + // sockaddr_un instead of sockaddr_storage here is ok. + struct sockaddr_un client_addr; + socklen_t client_addr_len; + client_addr_len = sizeof(client_addr); + + // Accept incoming connection. This will create a separate socket for + // handling this specific connection. + int fd2 = accept(sockfd_, static_cast(&client_addr), + &client_addr_len); + + // And now create an object that represents that new connection. + CommandSocketPtr conn(new ConnectionSocket(fd2)); + + // Not sure if this is really needed, but let's set it to non-blocking + // mode. + fcntl(fd2, F_SETFL, O_NONBLOCK); + + // Remember this socket descriptor. It will be needed when we shut down + // the server. + CommandMgr::instance().addConnection(conn); + + LOG_INFO(command_logger, COMMAND_SOCKET_CONNECTION_OPENED).arg(fd2) + .arg(sockfd_); } - if (type->stringValue() == "unix") { - return (createUnixSocket(name->stringValue())); - } else { - isc_throw(BadSocketInfo, "Specified socket type ('" + type->stringValue() - + "') is not supported."); + // This method is called when we shutdown the connection. + void close() { + LOG_INFO(command_logger, COMMAND_SOCKET_UNIX_CLOSE).arg(sockfd_) + .arg(filename_); + + isc::dhcp::IfaceMgr::instance().deleteExternalSocket(sockfd_); + + ::close(sockfd_); + remove(filename_.c_str()); } -} -void CommandSocketFactory::close(int socket_fd, - const isc::data::ConstElementPtr& socket_info) { + std::string filename_; +}; +CommandSocketPtr +CommandSocketFactory::create(const isc::data::ConstElementPtr& socket_info) { if(!socket_info) { isc_throw(BadSocketInfo, "Missing socket_info parameters, can't create socket."); } @@ -115,13 +171,8 @@ void CommandSocketFactory::close(int socket_fd, 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())); + return (CommandSocketPtr(new UnixCommandSocket(socket_info))); } 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 efa7cd5a8d..ea5b2da62c 100644 --- a/src/lib/config/command_socket_factory.h +++ b/src/lib/config/command_socket_factory.h @@ -16,25 +16,11 @@ #define COMMAND_SOCKET_FACTORY_H #include +#include 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. @@ -52,19 +38,9 @@ public: /// /// @param socket_info structure that describes the socket /// @return socket descriptor - static int create(const isc::data::ConstElementPtr& socket_info); - - /// @brief Closes specified socket - /// - /// In most cases it will be a simple close() call, but in more complex - /// (e.g. https) it may perform certain shutdown operations before - /// closing. - /// @param socket_fd file descriptor of the socket - /// @param socket_info structure that was used to open the socket - static void close(int socket_fd, const isc::data::ConstElementPtr& socket_info); + static CommandSocketPtr create(const isc::data::ConstElementPtr& socket_info); }; - }; }; diff --git a/src/lib/config/tests/command_socket_factory_unittests.cc b/src/lib/config/tests/command_socket_factory_unittests.cc index 65ab09d937..cafe285328 100644 --- a/src/lib/config/tests/command_socket_factory_unittests.cc +++ b/src/lib/config/tests/command_socket_factory_unittests.cc @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -65,11 +66,12 @@ TEST_F(CommandSocketFactoryTest, unixCreate) { 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); + CommandSocketPtr sock; + EXPECT_NO_THROW(sock = CommandSocketFactory::create(socket_info)); + ASSERT_TRUE(sock); + EXPECT_NE(-1, sock->getFD()); // It should be possible to close the socket. - EXPECT_NO_THROW(CommandSocketFactory::close(fd, socket_info)); + EXPECT_NO_THROW(sock->close()); }