From: Thomas Markwalder Date: Thu, 30 Jan 2020 19:23:53 +0000 (-0500) Subject: [#1097] CommandMgr now handles orphaned control sockets X-Git-Tag: Kea-1.7.5~104 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cd7b2807f421a97d3eef9e79962c9b0f46daee8e;p=thirdparty%2Fkea.git [#1097] CommandMgr now handles orphaned control sockets 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 --- diff --git a/ChangeLog b/ChangeLog index 17676e3127..2adddf1b64 100644 --- 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 diff --git a/src/lib/config/command_mgr.cc b/src/lib/config/command_mgr.cc index 8b491926cc..0ff2c5d850 100644 --- a/src/lib/config/command_mgr.cc +++ b/src/lib/config/command_mgr.cc @@ -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 #include #include +#include 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(::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(::remove(impl_->socket_name_.c_str())); + static_cast(::remove(impl_->getLockName().c_str())); } // Stop all connections which can be closed. The only connection that won't diff --git a/src/lib/config/tests/command_mgr_unittests.cc b/src/lib/config/tests/command_mgr_unittests.cc index 63fcaf0fcb..419cdf3cfd 100644 --- a/src/lib/config/tests/command_mgr_unittests.cc +++ b/src/lib/config/tests/command_mgr_unittests.cc @@ -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 #include #include +#include #include #include @@ -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()); +}