aboutsummaryrefslogtreecommitdiffstats
path: root/vespalib
diff options
context:
space:
mode:
authorHaavard <havardpe@yahoo-inc.com>2017-04-10 14:14:11 +0000
committerHaavard <havardpe@yahoo-inc.com>2017-04-10 15:18:52 +0000
commit0a38954fff6924fbebd09c7a741fb6c435d79f40 (patch)
tree5dce8c9ac91af1d103834020bb73f07ee21dee70 /vespalib
parent883f2835cb52153ec5824d18454cf39040ec2f75 (diff)
automatically clean up socket files
Diffstat (limited to 'vespalib')
-rw-r--r--vespalib/src/tests/net/socket/socket_test.cpp107
-rw-r--r--vespalib/src/vespa/vespalib/net/server_socket.cpp32
-rw-r--r--vespalib/src/vespa/vespalib/net/server_socket.h4
3 files changed, 121 insertions, 22 deletions
diff --git a/vespalib/src/tests/net/socket/socket_test.cpp b/vespalib/src/tests/net/socket/socket_test.cpp
index 44380f7d31e..5447f0de9bf 100644
--- a/vespalib/src/tests/net/socket/socket_test.cpp
+++ b/vespalib/src/tests/net/socket/socket_test.cpp
@@ -10,6 +10,34 @@
using namespace vespalib;
+bool is_socket(const vespalib::string &path) {
+ struct stat info;
+ if (path.empty() || (lstat(path.c_str(), &info) != 0)) {
+ return false;
+ }
+ return S_ISSOCK(info.st_mode);
+}
+
+bool is_file(const vespalib::string &path) {
+ struct stat info;
+ if (path.empty() || (lstat(path.c_str(), &info) != 0)) {
+ return false;
+ }
+ return S_ISREG(info.st_mode);
+}
+
+void remove_file(const vespalib::string &path) {
+ unlink(path.c_str());
+}
+
+void replace_file(const vespalib::string &path, const vespalib::string &data) {
+ remove_file(path);
+ int fd = creat(path.c_str(), 0600);
+ ASSERT_NOT_EQUAL(fd, -1);
+ ASSERT_EQUAL(write(fd, data.data(), data.size()), data.size());
+ close(fd);
+}
+
vespalib::string get_meta(const SocketAddress &addr) {
vespalib::string meta;
if (addr.is_ipv4()) {
@@ -115,16 +143,23 @@ TEST("local client/server addresses") {
}
struct ServerWrapper {
- ServerSocket::UP server = ServerSocket::listen(SocketSpec::from_port(0));
+ ServerSocket::UP server;
+ ServerWrapper(const vespalib::string &spec) : server(ServerSocket::listen(SocketSpec(spec))) {}
};
-TEST_MT_F("require that basic socket io works", 2, ServerWrapper) {
+TEST_MT_F("require that basic socket io works", 2, ServerWrapper("tcp/0")) {
+ bool is_server = (thread_id == 0);
+ Socket::UP socket = connect_sockets(is_server, *f1.server);
+ TEST_DO(verify_socket_io(is_server, *socket));
+}
+
+TEST_MT_F("require that basic unix domain socket io works", 2, ServerWrapper("ipc/file:my_socket")) {
bool is_server = (thread_id == 0);
Socket::UP socket = connect_sockets(is_server, *f1.server);
TEST_DO(verify_socket_io(is_server, *socket));
}
-TEST_MT_F("require that server accept can be interrupted", 2, ServerWrapper) {
+TEST_MT_F("require that server accept can be interrupted", 2, ServerWrapper("tcp/0")) {
bool is_server = (thread_id == 0);
if (is_server) {
fprintf(stderr, "--> calling accept\n");
@@ -138,25 +173,57 @@ TEST_MT_F("require that server accept can be interrupted", 2, ServerWrapper) {
}
}
-struct IpcServerWrapper {
- vespalib::string server_path;
- ServerSocket::UP server;
- IpcServerWrapper(const vespalib::string &server_path_in)
- : server_path(server_path_in), server()
+TEST("require that socket file is removed by server socket when destructed") {
+ remove_file("my_socket");
+ ServerSocket::UP server = ServerSocket::listen(SocketSpec("ipc/file:my_socket"));
+ EXPECT_TRUE(server->valid());
+ EXPECT_TRUE(is_socket("my_socket"));
+ server.reset();
+ EXPECT_TRUE(!is_socket("my_socket"));
+}
+
+TEST("require that socket file is only removed on destruction if it is a socket") {
+ remove_file("my_socket");
+ ServerSocket::UP server = ServerSocket::listen(SocketSpec("ipc/file:my_socket"));
+ EXPECT_TRUE(server->valid());
+ EXPECT_TRUE(is_socket("my_socket"));
+ replace_file("my_socket", "hello\n");
+ server.reset();
+ EXPECT_TRUE(is_file("my_socket"));
+ remove_file("my_socket");
+}
+
+TEST("require that a server socket will fail to listen to a path that is already a regular file") {
+ replace_file("my_socket", "hello\n");
+ ServerSocket::UP server = ServerSocket::listen(SocketSpec("ipc/file:my_socket"));
+ EXPECT_TRUE(!server->valid());
+ server.reset();
+ EXPECT_TRUE(is_file("my_socket"));
+ remove_file("my_socket");
+}
+
+TEST("require that a server socket will fail to listen to a path that is already taken by another server") {
+ remove_file("my_socket");
+ ServerSocket::UP server1 = ServerSocket::listen(SocketSpec("ipc/file:my_socket"));
+ ServerSocket::UP server2 = ServerSocket::listen(SocketSpec("ipc/file:my_socket"));
+ EXPECT_TRUE(server1->valid());
+ EXPECT_TRUE(!server2->valid());
+ EXPECT_TRUE(is_socket("my_socket"));
+ server1.reset();
+ EXPECT_TRUE(!is_socket("my_socket"));
+}
+
+TEST("require that a server socket will remove an old socket file if it cannot be connected to") {
+ remove_file("my_socket");
{
- unlink(server_path.c_str());
- server = ServerSocket::listen(SocketSpec::from_path(server_path));
- }
- ~IpcServerWrapper() {
- server.reset();
- unlink(server_path.c_str());
+ SocketHandle server_handle = SocketAddress::from_path("my_socket").listen();
+ EXPECT_TRUE(is_socket("my_socket"));
}
-};
-
-TEST_MT_F("require that basic unix domain socket io works", 2, IpcServerWrapper("my_socket")) {
- bool is_server = (thread_id == 0);
- Socket::UP socket = connect_sockets(is_server, *f1.server);
- TEST_DO(verify_socket_io(is_server, *socket));
+ EXPECT_TRUE(is_socket("my_socket"));
+ ServerSocket::UP server = ServerSocket::listen(SocketSpec("ipc/file:my_socket"));
+ EXPECT_TRUE(server->valid());
+ server.reset();
+ EXPECT_TRUE(!is_socket("my_socket"));
}
TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vespalib/src/vespa/vespalib/net/server_socket.cpp b/vespalib/src/vespa/vespalib/net/server_socket.cpp
index 5fe863b412f..04be8fc8888 100644
--- a/vespalib/src/vespa/vespalib/net/server_socket.cpp
+++ b/vespalib/src/vespa/vespalib/net/server_socket.cpp
@@ -1,11 +1,34 @@
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
#include "server_socket.h"
#include "socket_spec.h"
+#include <vespa/log/log.h>
+LOG_SETUP(".vespalib.net.server_socket");
+
namespace vespalib {
+bool is_socket(const vespalib::string &path) {
+ struct stat info;
+ if (path.empty() || (lstat(path.c_str(), &info) != 0)) {
+ return false;
+ }
+ return S_ISSOCK(info.st_mode);
+}
+
+ServerSocket::ServerSocket(SocketHandle handle)
+ : _handle(std::move(handle)),
+ _path(SocketAddress::address_of(_handle.get()).path())
+{
+}
+
+ServerSocket::~ServerSocket()
+{
+ if (is_socket(_path)) {
+ unlink(_path.c_str());
+ }
+}
+
SocketAddress
ServerSocket::address() const
{
@@ -31,6 +54,13 @@ ServerSocket::UP
ServerSocket::listen(const SocketSpec &spec)
{
SocketHandle handle = spec.server_address().listen();
+ if (!handle.valid() && is_socket(spec.path())) {
+ if (!spec.client_address().connect().valid()) {
+ LOG(warning, "removing old socket: '%s'", spec.path().c_str());
+ unlink(spec.path().c_str());
+ handle = spec.server_address().listen();
+ }
+ }
return std::make_unique<ServerSocket>(std::move(handle));
}
diff --git a/vespalib/src/vespa/vespalib/net/server_socket.h b/vespalib/src/vespa/vespalib/net/server_socket.h
index ac6b504522a..1c4f41343d7 100644
--- a/vespalib/src/vespa/vespalib/net/server_socket.h
+++ b/vespalib/src/vespa/vespalib/net/server_socket.h
@@ -15,12 +15,14 @@ class ServerSocket
{
private:
SocketHandle _handle;
+ vespalib::string _path;
public:
typedef std::unique_ptr<ServerSocket> UP;
ServerSocket(const ServerSocket &rhs) = delete;
ServerSocket &operator=(const ServerSocket &rhs) = delete;
- explicit ServerSocket(SocketHandle handle) : _handle(std::move(handle)) {}
+ explicit ServerSocket(SocketHandle handle);
+ ~ServerSocket();
bool valid() const { return _handle.valid(); }
SocketAddress address() const;
void shutdown();