diff options
author | Haavard <havardpe@yahoo-inc.com> | 2017-04-10 14:14:11 +0000 |
---|---|---|
committer | Haavard <havardpe@yahoo-inc.com> | 2017-04-10 15:18:52 +0000 |
commit | 0a38954fff6924fbebd09c7a741fb6c435d79f40 (patch) | |
tree | 5dce8c9ac91af1d103834020bb73f07ee21dee70 /vespalib/src | |
parent | 883f2835cb52153ec5824d18454cf39040ec2f75 (diff) |
automatically clean up socket files
Diffstat (limited to 'vespalib/src')
-rw-r--r-- | vespalib/src/tests/net/socket/socket_test.cpp | 107 | ||||
-rw-r--r-- | vespalib/src/vespa/vespalib/net/server_socket.cpp | 32 | ||||
-rw-r--r-- | vespalib/src/vespa/vespalib/net/server_socket.h | 4 |
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(); |