Files
FEX-Emu--FEX/Source/Steam/ServerManager.cpp
T
Ryan Houdek a44627df29 Steamrt/ServerManager: Change loop over to a blocking read
Discard any data and wait for EOF for now. That way we know when to
exit.
2025-12-09 13:08:01 -08:00

112 lines
3.1 KiB
C++

// SPDX-License-Identifier: MIT
#include "PortabilityInfo.h"
#include "Common/FEXServerClient.h"
#include <cstdio>
#include <errno.h>
#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;
// Do a blocking read, discarding any written data and wait for EOF.
while (true) {
char buf[4096];
auto read_len = ::read(STDIN_FILENO, buf, sizeof(buf));
if (read_len < 0) {
if (errno == EINTR || errno == EAGAIN) {
// Interrupted, try again.
continue;
} else {
// Error on read.
break;
}
} else if (read_len == 0) {
// EOF
break;
}
}
// Terminating will clean-up.
return 0;
}