mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 22:00:16 +02:00
208 lines
5.7 KiB
C++
208 lines
5.7 KiB
C++
// SPDX-License-Identifier: MIT
|
|
#include "ArgumentLoader.h"
|
|
#include "Logger.h"
|
|
#include "PipeScanner.h"
|
|
#include "ProcessPipe.h"
|
|
#include "SquashFS.h"
|
|
#include "Common/Config.h"
|
|
#include "Common/FEXServerClient.h"
|
|
|
|
#include <chrono>
|
|
#include <dirent.h>
|
|
#include <fcntl.h>
|
|
#include <filesystem>
|
|
#include <iterator>
|
|
#include <mutex>
|
|
#include <poll.h>
|
|
#include <sys/prctl.h>
|
|
#include <sys/signal.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <sys/un.h>
|
|
#include <sys/wait.h>
|
|
#include <thread>
|
|
#include <unistd.h>
|
|
|
|
namespace Logging {
|
|
void MsgHandler(LogMan::DebugLevels Level, char const *Message) {
|
|
const auto Output = fmt::format("[{}] {}\n", LogMan::DebugLevelStr(Level), Message);
|
|
write(STDOUT_FILENO, Output.c_str(), Output.size());
|
|
}
|
|
|
|
void AssertHandler(char const *Message) {
|
|
const auto Output = fmt::format("[ASSERT] {}\n", Message);
|
|
write(STDOUT_FILENO, Output.c_str(), Output.size());
|
|
}
|
|
|
|
void ClientMsgHandler(int FD, uint64_t Timestamp, uint32_t PID, uint32_t TID, uint32_t Level, const char* Msg) {
|
|
const auto Output = fmt::format("[{}][{}][{}.{}] {}\n", LogMan::DebugLevelStr(Level), Timestamp, PID, TID, Msg);
|
|
write(STDERR_FILENO, Output.c_str(), Output.size());
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
void ActionHandler(int sig, siginfo_t *info, void *context) {
|
|
// FEX_TODO("Fix this");
|
|
if (sig == SIGINT) {
|
|
// Someone trying to kill us. Shutdown.
|
|
ProcessPipe::Shutdown();
|
|
return;
|
|
}
|
|
_exit(1);
|
|
}
|
|
|
|
void ActionIgnore(int sig, siginfo_t *info, void *context) {
|
|
}
|
|
|
|
void SetupSignals() {
|
|
// Setup our signal handlers now so we can capture some events
|
|
struct sigaction act{};
|
|
act.sa_sigaction = ActionHandler;
|
|
act.sa_flags = SA_SIGINFO;
|
|
|
|
// SIGTERM if something is trying to terminate us
|
|
sigaction(SIGTERM, &act, nullptr);
|
|
// SIGINT if something is trying to terminate us
|
|
sigaction(SIGINT, &act, nullptr);
|
|
|
|
// SIGUSR1 just to interrupt syscalls
|
|
act.sa_sigaction = ActionIgnore;
|
|
sigaction(SIGUSR1, &act, nullptr);
|
|
|
|
// Ignore SIGPIPE, we will be checking for pipe closure which could send this signal
|
|
signal(SIGPIPE, SIG_IGN);
|
|
// SIGCHLD if squashfuse exits early.
|
|
// Ignore it for now
|
|
signal(SIGCHLD, SIG_IGN);
|
|
}
|
|
|
|
/**
|
|
* @brief Deparents itself by forking and terminating the parent process.
|
|
*/
|
|
void DeparentSelf() {
|
|
auto SystemdEnv = getenv("INVOCATION_ID");
|
|
if (SystemdEnv) {
|
|
// If FEXServer was launched through systemd then don't deparent, otherwise systemd kills the entire server.
|
|
return;
|
|
}
|
|
|
|
pid_t pid = fork();
|
|
|
|
if (pid != 0) {
|
|
// Parent is leaving to force this process to deparent itself
|
|
// This lets this process become the child of whatever the reaper parent is
|
|
_exit(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv, char **const envp) {
|
|
auto Options = FEXServer::Config::Load(argc, argv);
|
|
|
|
SetupSignals();
|
|
|
|
if (Options.Foreground) {
|
|
LogMan::Throw::InstallHandler(Logging::AssertHandler);
|
|
LogMan::Msg::InstallHandler(Logging::MsgHandler);
|
|
}
|
|
|
|
// Scan for any incoming pipes
|
|
// We will close these later
|
|
PipeScanner::ScanForPipes();
|
|
|
|
if (!Options.Foreground) {
|
|
DeparentSelf();
|
|
}
|
|
|
|
FEX::Config::LoadConfig(
|
|
true,
|
|
false,
|
|
argc, argv, envp,
|
|
false, {}
|
|
);
|
|
|
|
// Reload the meta layer
|
|
FEXCore::Config::ReloadMetaLayer();
|
|
|
|
if (Options.Wait) {
|
|
int ServerPipe = FEXServerClient::ConnectToServer();
|
|
if (ServerPipe != -1) {
|
|
int FEXServerPID = FEXServerClient::RequestPIDFD(ServerPipe);
|
|
close(ServerPipe);
|
|
if (FEXServerPID != -1) {
|
|
LogMan::Msg::IFmt("[FEXServer] Waiting for FEXServer to close");
|
|
// We can't use waitid (P_PIDFD) here because the active FEXServer isn't a child of this process.
|
|
// Use poll instead which will return once the pidfd closes.
|
|
pollfd PollFD;
|
|
PollFD.fd = FEXServerPID;
|
|
PollFD.events = POLLIN | POLLOUT | POLLRDHUP | POLLERR | POLLHUP | POLLNVAL;
|
|
|
|
// Wait for a result on the pipe that isn't EINTR
|
|
while (poll(&PollFD, 1, -1) == -1 && errno == EINTR);
|
|
|
|
LogMan::Msg::IFmt("[FEXServer] FEXServer shutdown");
|
|
}
|
|
PipeScanner::ClosePipes();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (Options.Kill) {
|
|
int ServerPipe = FEXServerClient::ConnectToServer();
|
|
|
|
if (ServerPipe != -1) {
|
|
FEXServerClient::RequestServerKill(ServerPipe);
|
|
LogMan::Msg::DFmt("[FEXServer] Sent kill packet");
|
|
PipeScanner::ClosePipes();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (!ProcessPipe::InitializeServerPipe()) {
|
|
// Someone else already owns the FEXServer pipe
|
|
PipeScanner::ClosePipes();
|
|
return -1;
|
|
}
|
|
|
|
if (!ProcessPipe::InitializeServerSocket()) {
|
|
// Couldn't create server socket for some reason
|
|
PipeScanner::ClosePipes();
|
|
return -1;
|
|
}
|
|
|
|
// Switch this process over to a new session id
|
|
// Probably not required but allows this to become the process group leader of its session
|
|
::setsid();
|
|
|
|
// Set process as a subreaper so subprocesses can't escape
|
|
::prctl(PR_SET_CHILD_SUBREAPER, 1);
|
|
|
|
if (Options.Foreground) {
|
|
// Only start a log thread if we are in the foreground.
|
|
// Prevents FEXInterpreter from trying to log to nothing.
|
|
Logger::StartLogThread();
|
|
}
|
|
|
|
if (!SquashFS::InitializeSquashFS()) {
|
|
LogMan::Msg::DFmt("[FEXServer] Couldn't mount squashfs");
|
|
return -1;
|
|
}
|
|
|
|
// Close the pipes we found at the start
|
|
// This will let FEXInterpreter know we are ready
|
|
PipeScanner::ClosePipes();
|
|
|
|
ProcessPipe::SetConfiguration(Options.Foreground, Options.PersistentTimeout ?: 10);
|
|
|
|
// Actually spin up the request thread.
|
|
// Any applications that were waiting for the socket to accept will then go through here.
|
|
ProcessPipe::WaitForRequests();
|
|
|
|
SquashFS::UnmountRootFS();
|
|
|
|
Logger::Shutdown();
|
|
|
|
return 0;
|
|
}
|