mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-08 22:00:19 +02:00
Closing the original stdout is how we signal to a larger compatibility tool (in practice pressure-vessel) that either we are ready, or we have crashed: when every process that held the write end open has closed it, the compatibility tool sees EOF on the pipe. If the FEXServer inherits stdout and holds it open, then that state will never be reached. steamrt/tasks#870 Signed-off-by: Simon McVittie <smcv@collabora.com>
112 lines
3.0 KiB
C++
112 lines
3.0 KiB
C++
// SPDX-License-Identifier: MIT
|
|
#include "PortabilityInfo.h"
|
|
#include "Common/FEXServerClient.h"
|
|
|
|
#include <cstdio>
|
|
#include <unistd.h>
|
|
#include <poll.h>
|
|
|
|
void MsgHandler(LogMan::DebugLevels Level, const char* Message) {
|
|
const auto Style = fmt::text_style {};
|
|
const auto Output = fextl::fmt::format("{} {}\n", fmt::styled(LogMan::DebugLevelStr(Level), Style), Message);
|
|
write(STDERR_FILENO, Output.c_str(), Output.size());
|
|
fsync(STDERR_FILENO);
|
|
}
|
|
|
|
void AssertHandler(const char* Message) {
|
|
return MsgHandler(LogMan::ASSERT, Message);
|
|
}
|
|
|
|
void SignalPVToContinue(int* original_stdout) {
|
|
// Tell pressure-vessel that the startup was a success.
|
|
const auto ReadyMsg = "READY=1\n";
|
|
write(*original_stdout, ReadyMsg, strlen(ReadyMsg));
|
|
|
|
// pressure-vessel is waiting for EOF on STDOUT from this process to ensure it can run FEX processes.
|
|
close(*original_stdout);
|
|
*original_stdout = -1;
|
|
}
|
|
|
|
struct PipesType {
|
|
int read_pipe {-1};
|
|
int write_pipe {-1};
|
|
};
|
|
|
|
PipesType get_pipe() {
|
|
PipesType pipes {};
|
|
pipe(&pipes.read_pipe);
|
|
return pipes;
|
|
}
|
|
|
|
int main(int argc, const char** argv, char** const envp) {
|
|
LogMan::Throw::InstallHandler(AssertHandler);
|
|
LogMan::Msg::InstallHandler(MsgHandler);
|
|
|
|
const auto PortableInfo = FEX::ReadPortabilityInformation();
|
|
FEX::Config::LoadConfig({}, envp, PortableInfo);
|
|
|
|
// Reload the meta layer
|
|
FEXCore::Config::ReloadMetaLayer();
|
|
|
|
// Move the ready-indicator pipe from stdout to some other fd,
|
|
// and mark it so the FEXServer won't inherit it. Otherwise the FEXServer
|
|
// will hold it open, preventing pressure-vessel from detecting that
|
|
// we are ready.
|
|
int original_stdout = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, /* minimum fd = */ 3);
|
|
if (original_stdout < 0) {
|
|
perror("F_DUPFD_CLOEXEC");
|
|
return 126;
|
|
}
|
|
// Replace stdout with a copy of our original stderr.
|
|
if (dup2(STDERR_FILENO, STDOUT_FILENO) != STDOUT_FILENO) {
|
|
perror("dup2");
|
|
return 126;
|
|
}
|
|
|
|
auto pipes = get_pipe();
|
|
|
|
// Set the write side to close on exec.
|
|
fcntl(pipes.write_pipe, F_SETFD, FD_CLOEXEC);
|
|
|
|
// Give the read end of the pipe to FEXServer.
|
|
auto ServerFD = FEXServerClient::StartServer(PortableInfo.InterpreterPath, pipes.read_pipe);
|
|
|
|
if (ServerFD == -1) {
|
|
perror("Couldn't start FEXServer");
|
|
return 126;
|
|
}
|
|
|
|
// FEXServer is now running. Tell PV to continue.
|
|
SignalPVToContinue(&original_stdout);
|
|
|
|
// Don't need the read pipe anymore.
|
|
close(pipes.read_pipe);
|
|
pipes.read_pipe = -1;
|
|
|
|
// Now that the server is started and watching our pipe, we can close the returned FD, as it'll stay open as long as the pipe is open.
|
|
close(ServerFD);
|
|
ServerFD = -1;
|
|
|
|
// stdin will be a pipe, so wait until that FD is closed.
|
|
while (true) {
|
|
pollfd p {
|
|
.fd = STDIN_FILENO,
|
|
.events = POLLRDHUP,
|
|
.revents = 0,
|
|
};
|
|
|
|
int events = poll(&p, 1, -1);
|
|
if (events == -1 && errno == EINTR) {
|
|
continue;
|
|
}
|
|
|
|
if (events > 0 && (p.revents & (POLLRDHUP | POLLERR | POLLHUP | POLLNVAL))) {
|
|
// Error or pressure-vessel hung-up.
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Terminating will clean-up.
|
|
return 0;
|
|
}
|