]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#1113] CommandMgr now handles orphaned control sockets
authorThomas Markwalder <tmark@isc.org>
Tue, 4 Feb 2020 19:49:05 +0000 (14:49 -0500)
committerThomas Markwalder <tmark@isc.org>
Tue, 4 Feb 2020 19:49:05 +0000 (14:49 -0500)
    Backport #1097 changes to v1_6_0.

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

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

index a3ddb917f2b514abcc591452e72c1e3dd310d66d..084617f84c7e29370c763f0a702c1b1983d6ab59 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+1661.  [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.6.1 released on Nov 20, 2019
 
 1660.  [bug]           tmark
index 8b491926cc3d5fd6f1ff5cd60cdc95abfacf2cdb..945b198c245f3b2aae12da88735bafdf9d4c62cc 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,28 @@ CommandMgrImpl::openCommandSocket(const isc::data::ConstElementPtr& socket_info)
 
     socket_name_ = name->stringValue();
 
+    // First let's open lock file.
+    std::string lock_name = getLockName();
+    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.
+    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 exists.
+    static_cast<void>(::remove(socket_name_.c_str()));
+
     LOG_INFO(command_logger, COMMAND_ACCEPTOR_START)
         .arg(socket_name_);
 
@@ -551,7 +579,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 +628,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..b607d3e4144ce275e043af1890205f8754366401 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
@@ -543,3 +543,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());
+}