From: Thomas Markwalder Date: Tue, 4 Feb 2020 19:49:05 +0000 (-0500) Subject: [#1113] CommandMgr now handles orphaned control sockets X-Git-Tag: Kea-1.6.2~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=43c53c6cf0fea1c13415c81c42ddd291a72dce33;p=thirdparty%2Fkea.git [#1113] CommandMgr now handles orphaned control sockets Backport #1097 changes to v1_6_0. Changes: ChangeLog src/lib/config/command_mgr.cc src/lib/config/tests/command_mgr_unittests.cc --- diff --git a/ChangeLog b/ChangeLog index a3ddb917f2..084617f84c 100644 --- 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 diff --git a/src/lib/config/command_mgr.cc b/src/lib/config/command_mgr.cc index 8b491926cc..945b198c24 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,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(::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(::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..b607d3e414 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 @@ -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()); +}