Move all plugin group handling boilerplate

This commit is contained in:
Robbert van der Helm
2020-05-19 15:29:48 +02:00
parent daad6f2f00
commit 6d6d928838
4 changed files with 258 additions and 100 deletions
+21 -100
View File
@@ -16,12 +16,7 @@
#include "boost-fix.h"
#include <boost/asio/read_until.hpp>
#include <boost/asio/streambuf.hpp>
#include <boost/filesystem.hpp>
#include <iostream>
#include <regex>
#include <thread>
// Generated inside of build directory
#include <src/common/config/config.h>
@@ -30,27 +25,6 @@
#include "bridges/group.h"
#include "bridges/vst2.h"
// FIXME: `std::filesystem` is broken in wineg++, at least under Wine 5.8. Any
// path operation will thrown an encoding related error.
namespace fs = boost::filesystem;
// TODO: Move most plumbing to another file
/**
* Create a logger prefix containing the group name based on the socket path.
*/
std::string create_logger_prefix(const fs::path& socket_path);
/**
* Continuously Read from a stream and write the lines to a logger instance.
*
* TODO: Merge this with the other similar function in `PluginBridge`
*/
void log_lines(Logger& logger,
boost::asio::posix::stream_descriptor& pipe,
boost::asio::streambuf& buffer,
std::string prefix);
/**
* This works very similar to the host application defined in
* `individual-host.cpp`, but instead of just loading a single plugin this will
@@ -82,84 +56,31 @@ int __cdecl main(int argc, char* argv[]) {
const std::string group_socket_endpoint_path(argv[1]);
// TODO: Before doing anything, try listening on the socket and fail
// silently (or log a message?) if another application is already
// listening on the socket. This way we don't need any complicated
// inter-process synchronization to ensure that there is a single
// active group host listening for this group.
// Has to be initialized before redirecting the STDIO streams
Logger logger = Logger::create_from_environment(
create_logger_prefix(group_socket_endpoint_path));
// Redirect this process's STDOUT and STDERR streams to a pipe so we can
// process the output and redirect it to a logger. Needed to capture Wine
// debug output, since this process will likely outlive the yabridge
// instance that originally spawned it.
boost::asio::io_context io_context;
boost::asio::streambuf stdout_buffer;
boost::asio::streambuf stderr_buffer;
StdIoCapture stdout_redirect(io_context, STDOUT_FILENO);
StdIoCapture stderr_redirect(io_context, STDERR_FILENO);
log_lines(logger, stdout_redirect.pipe, stdout_buffer, "[STDOUT] ");
log_lines(logger, stderr_redirect.pipe, stderr_buffer, "[STDERR] ");
std::thread io_handler([&]() { io_context.run(); });
logger.log("Initializing yabridge group host version " +
std::string(yabridge_git_version)
// TODO: Catch exception during initialization when another process is
// already listening on the socket. Make sure to print a more useful
// message instead.
try {
std::cerr << "Initializing yabridge group host version "
<< yabridge_git_version
#ifdef __i386__
+ " (32-bit compatibility mode)"
<< " (32-bit compatibility mode)"
#endif
);
<< std::endl;
// TODO: Remove debug prints
printf("This should be caught now!\n");
std::cerr << "This too!" << std::endl;
GroupBridge bridge(group_socket_endpoint_path);
// TODO: After initializing, listen for connections and spawn plugins
// the exact same way as what happens in `individual-host.cpp`
// TODO: Allow this process to exit when the last plugin exits. Make sure
// that that doesn't cause any race conditions.
// Blocks the main thread until all plugins have exited
bridge.handle_incoming_connections();
} catch (const std::runtime_error& error) {
// Even though the process will likely outlive the yabridge instance
// that spawns it, we can still print any initialization messages and
// errors to STDERR since at this point there will still be a yabridge
// instance capturing this process's output.
// TODO: Check if this is printed on the right stream
std::cerr << "Error while initializing the group host process:"
<< std::endl;
std::cerr << error.what() << std::endl;
// TODO: This usleep() is just to ensure that the second print to stderr
// also gets processed before stopping the IO context since we're
// immediately stopping it after starting. This would not needed in
// normal use.
usleep(1000);
io_context.stop();
io_handler.join();
}
void log_lines(Logger& logger,
boost::asio::posix::stream_descriptor& pipe,
boost::asio::streambuf& buffer,
std::string prefix) {
boost::asio::async_read_until(pipe, buffer, '\n',
[&, prefix](const auto&, size_t) {
std::string line;
std::getline(std::istream(&buffer), line);
logger.log(prefix + line);
log_lines(logger, pipe, buffer, prefix);
});
}
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',
// where Wine prefix ID is just Wine prefix ran through `std::hash` to
// prevent collisions without needing complicated filenames. We want to
// extract the group name.
std::string socket_name =
socket_path.filename().replace_extension().string();
std::smatch group_match;
std::regex group_regexp("^yabridge-group-(.*)-[^-]+-[^-]+$",
std::regex::ECMAScript);
if (std::regex_match(socket_name, group_match, group_regexp)) {
socket_name = group_match[1].str();
return 1;
}
return "[" + socket_name + "] ";
}