]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#1097] CommandMgr now handles orphaned control sockets
authorThomas Markwalder <tmark@isc.org>
Thu, 30 Jan 2020 19:23:53 +0000 (14:23 -0500)
committerThomas Markwalder <tmark@isc.org>
Thu, 30 Jan 2020 20:37:22 +0000 (15:37 -0500)
Added a ChangeLog entry.

src/lib/config/command_mgr.cc
    CommandMgrImpl::openCommandSocket() - now attempts to open and
    lock a lock file whose name is derived from the control socket
    name.  If the lock cannot be established, it is assumed that the
    socket is in use.

    CommandMgr::closeCommandSocket() - removes the lock file after
    the socket file is removed.

src/lib/config/tests/command_mgr_unittests.cc
    TEST_F(CommandMgrTest, exclusiveOpen) - new unit test

ChangeLog
src/lib/config/command_mgr.cc
src/lib/config/tests/command_mgr_unittests.cc

index 17676e31273346dc432381698e8569b7c107c183..2adddf1b64ec066ea20f0c05097dde11a3391722 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+1715.  [bug]           tmark
+       Kea servers now detect and remove orphaned control channel
+       sockets.  This corrects a failure of the servers to restart
+       with an error of "address already in use" following a fatal
+       loss of database connecivity.
+       (Gitlab #1097)
+
 Kea 1.7.4 released on Jan 29, 2020
 
 1714.  [build]         razvan
index 8b491926cc3d5fd6f1ff5cd60cdc95abfacf2cdb..0ff2c5d850dda224cbe4d39ba63951810311e00d 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2015-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2020 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -24,6 +24,7 @@
 #include <boost/enable_shared_from_this.hpp>
 #include <array>
 #include <unistd.h>
+#include <sys/file.h>
 
 using namespace isc;
 using namespace isc::asiolink;
@@ -487,6 +488,11 @@ public:
     /// @brief Asynchronously accepts next connection.
     void doAccept();
 
+    /// @brief Returns the lock file name
+    std::string getLockName() {
+       return(std::string(socket_name_ + ".lock"));
+    }
+
     /// @brief Pointer to the IO service used by the server process for running
     /// asynchronous tasks.
     IOServicePtr io_service_;
@@ -541,6 +547,30 @@ CommandMgrImpl::openCommandSocket(const isc::data::ConstElementPtr& socket_info)
 
     socket_name_ = name->stringValue();
 
+    // First let's open lock file.
+    std::string lock_name = getLockName();
+    errno = 0;
+    int lock_fd = open(lock_name.c_str(), O_RDONLY | O_CREAT, 0600);
+    if (lock_fd == -1) {
+        std::string errmsg = strerror(errno);
+        isc_throw(SocketError, "cannot create socket lockfile, "
+                  << lock_name  << ", : " << errmsg);
+    }
+
+    // Try to acquire lock. If we can't somebody else is actively
+    // using it.
+    errno = 0;
+    int ret = flock(lock_fd, LOCK_EX | LOCK_NB);
+    if (ret != 0) {
+        std::string errmsg = strerror(errno);
+        isc_throw(SocketError, "cannot lock socket lockfile, "
+                  << lock_name  << ", : " << errmsg);
+    }
+
+    // We have the lock, so let's remove the pre-existing socket
+    // file if it's exists.
+    static_cast<void>(::remove(socket_name_.c_str()));
+
     LOG_INFO(command_logger, COMMAND_ACCEPTOR_START)
         .arg(socket_name_);
 
@@ -551,7 +581,6 @@ CommandMgrImpl::openCommandSocket(const isc::data::ConstElementPtr& socket_info)
         acceptor_->open(endpoint);
         acceptor_->bind(endpoint);
         acceptor_->listen();
-
         // Install this socket in Interface Manager.
         isc::dhcp::IfaceMgr::instance().addExternalSocket(acceptor_->getNative(), 0);
 
@@ -601,6 +630,7 @@ void CommandMgr::closeCommandSocket() {
         isc::dhcp::IfaceMgr::instance().deleteExternalSocket(impl_->acceptor_->getNative());
         impl_->acceptor_->close();
         static_cast<void>(::remove(impl_->socket_name_.c_str()));
+        static_cast<void>(::remove(impl_->getLockName().c_str()));
     }
 
     // Stop all connections which can be closed. The only connection that won't
index 63fcaf0fcbd94ca5800d4745be3f07ebd9722358..419cdf3cfd093fecdd85a800915e6fd316917a7a 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2015-2019 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2020 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -17,6 +17,7 @@
 #include <hooks/hooks_manager.h>
 #include <hooks/callout_handle.h>
 #include <hooks/library_handle.h>
+#include <testutils/gtest_utils.h>
 #include <string>
 #include <vector>
 
@@ -543,3 +544,28 @@ TEST_F(CommandMgrTest, commandProcessedHookReplaceResponse) {
              "{ \"result\": 2, \"text\": \"'change-response' command not supported.\" }",
               processed_log_);
 }
+
+// Verifies that a socket cannot be concurrently opened more than once.
+TEST_F(CommandMgrTest, exclusiveOpen) {
+    // Pass in valid parameters.
+    ElementPtr socket_info = Element::createMap();
+    socket_info->set("socket-type", Element::create("unix"));
+    socket_info->set("socket-name", Element::create(getSocketPath()));
+
+    EXPECT_NO_THROW(CommandMgr::instance().openCommandSocket(socket_info));
+    EXPECT_GE(CommandMgr::instance().getControlSocketFD(), 0);
+
+    // Should not be able to open it twice.
+    EXPECT_THROW(CommandMgr::instance().openCommandSocket(socket_info),
+                 isc::config::SocketError);
+
+    // Now let's close it.
+    EXPECT_NO_THROW(CommandMgr::instance().closeCommandSocket());
+
+    // Should be able to re-open it now.
+    EXPECT_NO_THROW(CommandMgr::instance().openCommandSocket(socket_info));
+    EXPECT_GE(CommandMgr::instance().getControlSocketFD(), 0);
+
+    // Now let's close it.
+    EXPECT_NO_THROW(CommandMgr::instance().closeCommandSocket());
+}