Replace Boost.Process for the Wine plugin host

Now we use our own library for this, so we can drop the Boost.Filesystem
dependency after a couple more changes.
This commit is contained in:
Robbert van der Helm
2022-04-14 18:45:47 +02:00
parent f79b0fd146
commit 037d6ad5aa
6 changed files with 148 additions and 413 deletions
+34 -98
View File
@@ -19,10 +19,8 @@
#include <thread>
#include <asio/local/stream_protocol.hpp>
#include <asio/posix/stream_descriptor.hpp>
#include <asio/streambuf.hpp>
#include <boost/process/child.hpp>
#include <boost/process/extend.hpp>
#include <boost/process/io.hpp>
#include <ghc/filesystem.hpp>
#include "../common/communication/common.h"
@@ -59,108 +57,42 @@ class HostProcess {
*/
virtual void terminate() = 0;
/**
* Simple helper function around `boost::process::child` that launches the
* host application (`*.exe`) with some basic setup. This includes setting
* up the asynchronous pipes for STDIO redirection, closing file descriptors
* to prevent leaks, and wrapping everything in winedbg if we're compiling
* with `-Dwith-winedbg=true`. Keep in mind that winedbg does not handle
* arguments containing spaces, so most Windows paths will be split up into
* multiple arugments.
*/
template <typename... Args>
boost::process::child launch_host(ghc::filesystem::path host_path,
Args&&... args) {
return boost::process::child(
#ifdef WITH_WINEDBG
// This is set up for KDE Plasma. Other desktop environments and
// window managers require some slight modifications to spawn a
// detached terminal emulator. Alternatively, you can spawn
// `/usr/bin/winedbg` with the `--no-start` option to launch a gdb
// server and then connect to it from another terminal.
"/usr/bin/kstart5", "konsole", "--", "-e", "winedbg", "--gdb",
#ifdef WINEDBG_LEGACY_ARGUMENT_QUOTING
// Note the double quoting here. Old versions of winedbg didn't
// respect `argv` and instead expected a pre-quoted Win32 command
// line as its arguments.
"\"" + host_path.string() + ".so\"",
#else
host_path.string() + ".so",
#endif // WINEDBG_LEGACY_ARGUMENT_QUOTING
#else
// FIXME: Replace Boost.Filesystem
host_path.string(),
#endif // WITH_WINEDBG
// FIXME: This won't work with our patched async_pipe version
// boost::process::std_out = stdout_pipe_,
// boost::process::std_err = stderr_pipe_,
patched_async_pipe_out<1, -1>(stdout_pipe_),
patched_async_pipe_out<2, -1>(stderr_pipe_),
// NOTE: If the Wine process outlives the host, then it may cause
// issues if our process is still keeping the host's file
// descriptors alive that. This can prevent Ardour from
// restarting after an unexpected shutdown. Because of this we
// won't use `vfork()`, but instead we'll just manually close
// all non-STDIO file descriptors.
// HACK: If the `disable_pipes` option is enabled, then we'll
// redirect the plugin's output to a file instead of using
// pipes to blend it in with the rest of yabridge's output.
// This is for some reason necessary for ujam's plugins and
// all other plugins made with Gorilla Engine to function.
// Otherwise they'll print a nondescriptive `JS_EXEC_FAILED`
// error message.
boost::process::extend::on_exec_setup =
[this](auto& /*executor*/) {
const int max_fds = static_cast<int>(sysconf(_SC_OPEN_MAX));
for (int fd = STDERR_FILENO + 1; fd < max_fds; fd++) {
close(fd);
}
// See above
if (config_.disable_pipes) {
const int redirect_fd =
open(config_.disable_pipes->c_str(),
O_CREAT | O_APPEND | O_WRONLY, 0640);
assert(redirect_fd != -1);
dup2(redirect_fd, STDOUT_FILENO);
dup2(redirect_fd, STDERR_FILENO);
close(redirect_fd);
}
},
std::forward<Args>(args)...);
}
protected:
/**
* Initialize the host process by setting up the STDIO redirection.
* The actual process initialization and everything involved in that process
* is done in `launch_host()` since a new process may not be required
* when using plugin groups.
*
* @param io_context The IO context that the STDIO redurection will be
* handled on.
* @param logger The `Logger` instance the redirected STDIO streams will be
* written to.
* @param io_context The IO context that the STDIO redirection pipes will be
* bound to. The logging for these pipes is set up in `launch_host()`.
* @param sockets The socket endpoints that will be used for communication
* with the plugin. When the plugin shuts down, we'll close all of the
* sockets used by the plugin.
*/
HostProcess(asio::io_context& io_context,
Logger& logger,
const Configuration& config,
Sockets& sockets);
HostProcess(asio::io_context& io_context, Sockets& sockets);
/**
* The STDOUT stream of the Wine process we can forward to the logger.
* Helper function that launches the Wine host application (`*.exe`) with
* all of the correct environment setup. This includes setting the correct
* environment variables for the Wine prefix the plugin is in, setting up
* pipes or files for STDIO redirection, closing file descriptors to prevent
* leaks, and wrapping all of that in a terminal process running winedbg if
* we're compiling with `-Dwith-winedbg=true`. Keep in mind that winedbg
* does not handle arguments containing spaces, so most Windows paths will
* be split up into multiple arguments.
*
* @param logger The `Logger` instance the redirected STDIO streams will be
* written to.
* @param config The plugin's configuration, used to determine whether to
* use pipes or to redirect the output to a file instead.
* @param plugin_info Information about the plugin, used to determine the
* plugin's Wine prefix.
*/
patched_async_pipe stdout_pipe_;
/**
* The STDERR stream of the Wine process we can forward to the logger.
*/
patched_async_pipe stderr_pipe_;
/**
* The current plugin instance's configuration.
*/
const Configuration& config_;
Process::Handle launch_host(const ghc::filesystem::path& host_path,
std::initializer_list<std::string> args,
Logger& logger,
const Configuration& config,
const PluginInfo& plugin_info);
/**
* The associated sockets for the plugin we're hosting. This is used to
@@ -170,9 +102,13 @@ class HostProcess {
private:
/**
* The logger the Wine output will be written to.
* The STDOUT stream of the Wine process we can forward to the logger.
*/
Logger& logger_;
asio::posix::stream_descriptor stdout_pipe_;
/**
* The STDERR stream of the Wine process we can forward to the logger.
*/
asio::posix::stream_descriptor stderr_pipe_;
asio::streambuf stdout_buffer_;
asio::streambuf stderr_buffer_;
@@ -218,7 +154,7 @@ class IndividualHost : public HostProcess {
private:
const PluginInfo& plugin_info_;
ghc::filesystem::path host_path_;
boost::process::child host_;
Process::Handle handle_;
};
/**