Add handling for stale and active group sockets

This commit is contained in:
Robbert van der Helm
2020-05-22 18:19:37 +02:00
parent 5b4b62d7c4
commit 9fb7f1fc03
2 changed files with 56 additions and 2 deletions
+55 -1
View File
@@ -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-<group_name>-<wine_prefix_id>-<architecture>.sock',
+1 -1
View File
@@ -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-<group_name>-<wine_prefix_id>-<architecture>`
* `/tmp/yabridge-group-<group_name>-<wine_prefix_id>-<architecture>.sock`
* where `<wine_prefix_id>` is a numerical hash as explained in the
* `create_logger_prefix()` function in `./group.cpp`.
*