-// 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
#include <boost/enable_shared_from_this.hpp>
#include <array>
#include <unistd.h>
+#include <sys/file.h>
using namespace isc;
using namespace isc::asiolink;
/// @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_;
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_);
acceptor_->open(endpoint);
acceptor_->bind(endpoint);
acceptor_->listen();
-
// Install this socket in Interface Manager.
isc::dhcp::IfaceMgr::instance().addExternalSocket(acceptor_->getNative(), 0);
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
-// 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
"{ \"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());
+}