face: UnixStreamChannel: improved handling of existing socket files.
Add logic to prevent accidental removal of a socket file that belongs
to another (still running) NFD process.
Refs: #1360, #1367
Change-Id: Ibf755cd55e27f5a4cf165a0fbcdbf01fdb33005a
diff --git a/daemon/face/unix-stream-channel.cpp b/daemon/face/unix-stream-channel.cpp
index 15c5cd7..efdf700 100644
--- a/daemon/face/unix-stream-channel.cpp
+++ b/daemon/face/unix-stream-channel.cpp
@@ -36,18 +36,20 @@
UnixStreamChannel::UnixStreamChannel(const unix_stream::Endpoint& endpoint)
: m_endpoint(endpoint)
+ , m_isListening(false)
{
- this->setUri(FaceUri(endpoint));
+ setUri(FaceUri(endpoint));
}
UnixStreamChannel::~UnixStreamChannel()
{
- if (m_acceptor)
+ if (m_isListening)
{
// use the non-throwing variants during destruction
// and ignore any errors
boost::system::error_code error;
m_acceptor->close(error);
+ NFD_LOG_TRACE("[" << m_endpoint << "] Removing socket file");
boost::filesystem::remove(m_endpoint.path(), error);
}
}
@@ -57,21 +59,48 @@
const ConnectFailedCallback& onAcceptFailed,
int backlog/* = acceptor::max_connections*/)
{
- if (m_acceptor) // already listening
+ if (m_isListening) {
+ NFD_LOG_WARN("[" << m_endpoint << "] Already listening");
return;
+ }
namespace fs = boost::filesystem;
- fs::path p(m_endpoint.path());
- if (fs::symlink_status(p).type() == fs::socket_file)
+
+ fs::path socketPath(m_endpoint.path());
+ fs::file_type type = fs::symlink_status(socketPath).type();
+
+ if (type == fs::socket_file)
{
- // for safety, remove an already existing file only if it's a socket
- fs::remove(p);
+ boost::system::error_code error;
+ stream_protocol::socket socket(getGlobalIoService());
+ socket.connect(m_endpoint, error);
+ NFD_LOG_TRACE("[" << m_endpoint << "] connect() on existing socket file returned: "
+ + error.message());
+ if (!error)
+ {
+ // someone answered, leave the socket alone
+ throw Error("Socket file at " + m_endpoint.path()
+ + " belongs to another NFD process");
+ }
+ else if (error == boost::system::errc::connection_refused ||
+ error == boost::system::errc::timed_out)
+ {
+ // no one is listening on the remote side,
+ // we can safely remove the socket file
+ NFD_LOG_INFO("[" << m_endpoint << "] Removing stale socket file");
+ fs::remove(socketPath);
+ }
+ }
+ else if (type != fs::file_not_found)
+ {
+ throw Error(m_endpoint.path() + " already exists and is not a socket file");
}
m_acceptor = make_shared<stream_protocol::acceptor>(boost::ref(getGlobalIoService()));
- m_acceptor->open(m_endpoint.protocol());
+ m_acceptor->open();
m_acceptor->bind(m_endpoint);
m_acceptor->listen(backlog);
+ m_isListening = true;
if (::chmod(m_endpoint.path().c_str(), 0666) < 0)
{
@@ -80,17 +109,11 @@
shared_ptr<stream_protocol::socket> clientSocket =
make_shared<stream_protocol::socket>(boost::ref(getGlobalIoService()));
- m_acceptor->async_accept(*clientSocket,
- bind(&UnixStreamChannel::handleSuccessfulAccept, this, _1,
- clientSocket, onFaceCreated, onAcceptFailed));
-}
-void
-UnixStreamChannel::createFace(const shared_ptr<stream_protocol::socket>& socket,
- const FaceCreatedCallback& onFaceCreated)
-{
- shared_ptr<UnixStreamFace> face = make_shared<UnixStreamFace>(boost::cref(socket));
- onFaceCreated(face);
+ m_acceptor->async_accept(*clientSocket,
+ bind(&UnixStreamChannel::handleSuccessfulAccept, this,
+ boost::asio::placeholders::error, clientSocket,
+ onFaceCreated, onAcceptFailed));
}
void
@@ -103,23 +126,24 @@
if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
return;
- NFD_LOG_DEBUG("Connection failed: "
- << error.category().message(error.value()));
-
- onAcceptFailed("Connection failed: " +
- error.category().message(error.value()));
+ NFD_LOG_DEBUG("[" << m_endpoint << "] Connection failed: " << error.message());
+ onAcceptFailed("Connection failed: " + error.message());
return;
}
- // prepare accepting the next connection
+ NFD_LOG_DEBUG("[" << m_endpoint << "] << Incoming connection");
+
shared_ptr<stream_protocol::socket> clientSocket =
make_shared<stream_protocol::socket>(boost::ref(getGlobalIoService()));
- m_acceptor->async_accept(*clientSocket,
- bind(&UnixStreamChannel::handleSuccessfulAccept, this, _1,
- clientSocket, onFaceCreated, onAcceptFailed));
- NFD_LOG_DEBUG("[" << m_endpoint << "] << Incoming connection");
- createFace(socket, onFaceCreated);
+ // prepare accepting the next connection
+ m_acceptor->async_accept(*clientSocket,
+ bind(&UnixStreamChannel::handleSuccessfulAccept, this,
+ boost::asio::placeholders::error, clientSocket,
+ onFaceCreated, onAcceptFailed));
+
+ shared_ptr<UnixStreamFace> face = make_shared<UnixStreamFace>(boost::cref(socket));
+ onFaceCreated(face);
}
} // namespace nfd
diff --git a/daemon/face/unix-stream-channel.hpp b/daemon/face/unix-stream-channel.hpp
index 3f4be84..e706a53 100644
--- a/daemon/face/unix-stream-channel.hpp
+++ b/daemon/face/unix-stream-channel.hpp
@@ -79,10 +79,6 @@
private:
void
- createFace(const shared_ptr<boost::asio::local::stream_protocol::socket>& socket,
- const FaceCreatedCallback& onFaceCreated);
-
- void
handleSuccessfulAccept(const boost::system::error_code& error,
const shared_ptr<boost::asio::local::stream_protocol::socket>& socket,
const FaceCreatedCallback& onFaceCreated,
@@ -91,6 +87,7 @@
private:
unix_stream::Endpoint m_endpoint;
shared_ptr<boost::asio::local::stream_protocol::acceptor> m_acceptor;
+ bool m_isListening;
};
} // namespace nfd