From 9fb7f1fc0360bb7b4fdf006685dacc170c72e71e Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Fri, 22 May 2020 18:19:37 +0200 Subject: [PATCH] Add handling for stale and active group sockets --- src/wine-host/bridges/group.cpp | 56 ++++++++++++++++++++++++++++++++- src/wine-host/bridges/group.h | 2 +- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/wine-host/bridges/group.cpp b/src/wine-host/bridges/group.cpp index 9a6966b1..14fa9dcc 100644 --- a/src/wine-host/bridges/group.cpp +++ b/src/wine-host/bridges/group.cpp @@ -27,6 +27,26 @@ // path operation will thrown an encoding related error namespace fs = boost::filesystem; +/** + * Listen on the specified endpoint if no process is already listening there, + * otherwise throw. This is needed to handle these three situations: + * + * 1. The endpoint does not already exist, and we can simply create an endpoint. + * 2. The endpoint already exists but it is stale and no process is currently + * listening. In this case we can remove the file and start listening. + * 3. The endpoint already exists and another process is currently listening on + * it. In this situation we will throw immediately and we'll terminate this + * process. + * + * If anyone knows a better way to handle this, please let me know! + * + * @throw std::runtime_error If another process is already listening on the + * endpoint. + */ +boost::asio::local::stream_protocol::acceptor create_acceptor_if_inactive( + boost::asio::io_context& io_context, + boost::asio::local::stream_protocol::endpoint& endpoint); + /** * Create a logger prefix containing the group name based on the socket path. */ @@ -75,7 +95,8 @@ GroupBridge::GroupBridge(boost::filesystem::path group_socket_path) stdout_redirect(io_context, STDOUT_FILENO), stderr_redirect(io_context, STDERR_FILENO), group_socket_endpoint(group_socket_path.string()), - group_socket_acceptor(io_context, group_socket_endpoint) { + group_socket_acceptor( + create_acceptor_if_inactive(io_context, group_socket_endpoint)) { // Write this process's original STDOUT and STDERR streams to the logger async_log_pipe_lines(stdout_redirect.pipe, stdout_buffer, "[STDOUT] "); async_log_pipe_lines(stderr_redirect.pipe, stderr_buffer, "[STDERR] "); @@ -179,6 +200,39 @@ void GroupBridge::async_log_pipe_lines( }); } +boost::asio::local::stream_protocol::acceptor create_acceptor_if_inactive( + boost::asio::io_context& io_context, + boost::asio::local::stream_protocol::endpoint& endpoint) { + // First try to listen on the endpoint normally + try { + return boost::asio::local::stream_protocol::acceptor(io_context, + endpoint); + } catch (const boost::system::system_error& error) { + // If this failed, then either there is a stale socket file or another + // process is already is already listening. In the last case we will + // simply throw so the other process can handle the request. + std::ifstream open_sockets("/proc/net/unix"); + std::string endpoint_path = endpoint.path(); + for (std::string line; std::getline(open_sockets, line);) { + if (line.size() < endpoint_path.size()) { + continue; + } + + std::string file = line.substr(line.size() - endpoint_path.size()); + if (file == endpoint_path) { + // Another process is already listening, so we don't have to do + // anything + throw error; + } + } + + // At this point we can remove the stale socket and start listening + fs::remove(endpoint_path); + return boost::asio::local::stream_protocol::acceptor(io_context, + endpoint); + } +} + std::string create_logger_prefix(const fs::path& socket_path) { // The group socket filename will be in the format // '/tmp/yabridge-group---.sock', diff --git a/src/wine-host/bridges/group.h b/src/wine-host/bridges/group.h index 33b79b99..93039bef 100644 --- a/src/wine-host/bridges/group.h +++ b/src/wine-host/bridges/group.h @@ -104,7 +104,7 @@ class GroupBridge { * * @param gruop_socket_path The path to the group socket endpoint. This path * should be in the form of - * `/tmp/yabridge-group---` + * `/tmp/yabridge-group---.sock` * where `` is a numerical hash as explained in the * `create_logger_prefix()` function in `./group.cpp`. *