Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEXServer: Listen on both abstract & named sockets #4159

Merged
merged 4 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 52 additions & 11 deletions Source/Common/FEXServerClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,21 @@ fextl::string GetServerRootFSLockFile() {
}

fextl::string GetTempFolder() {
auto XDGRuntimeEnv = getenv("XDG_RUNTIME_DIR");
if (XDGRuntimeEnv) {
// If the XDG runtime directory works then use that.
return XDGRuntimeEnv;
const std::array<const char*, 5> Vars = {
"XDG_RUNTIME_DIR", "TMPDIR", "TMP", "TEMP", "TEMPDIR",
};

for (auto& Var : Vars) {
auto Path = getenv(Var);
if (Path) {
// If one of the env variable-driven paths works then use that.
return Path;
}
}
// Fallback to `/tmp/` if XDG_RUNTIME_DIR doesn't exist.

// Fallback to `/tmp/` if no env vars are set.
// Might not be ideal but we don't have much of a choice.
return fextl::string {std::filesystem::temp_directory_path().string()};
return fextl::string {"/tmp"};
}

fextl::string GetServerMountFolder() {
Expand Down Expand Up @@ -143,6 +150,24 @@ fextl::string GetServerSocketName() {
return ServerSocketPath;
}

fextl::string GetServerSocketPath() {
FEX_CONFIG_OPT(ServerSocketPath, SERVERSOCKETPATH);

auto name = ServerSocketPath();

if (name.starts_with("/")) {
return name;
}

auto Folder = GetTempFolder();

if (name.empty()) {
return fextl::fmt::format("{}/{}.FEXServer.Socket", Folder, ::geteuid());
} else {
return fextl::fmt::format("{}/{}", Folder, name);
}
}

int GetServerFD() {
return ServerFD;
}
Expand All @@ -153,7 +178,7 @@ int ConnectToServer(ConnectionOption ConnectionOption) {
// Create the initial unix socket
int SocketFD = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (SocketFD == -1) {
LogMan::Msg::EFmt("Couldn't open AF_UNIX socket {} {}", errno, strerror(errno));
LogMan::Msg::EFmt("Couldn't open AF_UNIX socket {}", errno);
return -1;
}

Expand All @@ -170,13 +195,29 @@ int ConnectToServer(ConnectionOption ConnectionOption) {

if (connect(SocketFD, reinterpret_cast<struct sockaddr*>(&addr), SizeOfAddr) == -1) {
if (ConnectionOption == ConnectionOption::Default || errno != ECONNREFUSED) {
LogMan::Msg::EFmt("Couldn't connect to FEXServer socket {} {} {}", ServerSocketName, errno, strerror(errno));
LogMan::Msg::EFmt("Couldn't connect to FEXServer socket {} {}", ServerSocketName, errno);
}
close(SocketFD);
return -1;
} else {
return SocketFD;
}

// Try again with a path-based socket, since abstract sockets will fail if we have been
// placed in a new netns as part of a sandbox.
auto ServerSocketPath = GetServerSocketPath();

SizeOfSocketString = std::min(ServerSocketPath.size(), sizeof(addr.sun_path) - 1);
strncpy(addr.sun_path, ServerSocketPath.data(), SizeOfSocketString);
SizeOfAddr = sizeof(addr.sun_family) + SizeOfSocketString;
if (connect(SocketFD, reinterpret_cast<struct sockaddr*>(&addr), SizeOfAddr) == -1) {
if (ConnectionOption == ConnectionOption::Default || (errno != ECONNREFUSED && errno != ENOENT)) {
LogMan::Msg::EFmt("Couldn't connect to FEXServer socket {} {}", ServerSocketPath, errno);
}
} else {
return SocketFD;
}

return SocketFD;
close(SocketFD);
return -1;
}

bool SetupClient(char* InterpreterPath) {
Expand Down
1 change: 1 addition & 0 deletions Source/Common/FEXServerClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ fextl::string GetServerRootFSLockFile();
fextl::string GetTempFolder();
fextl::string GetServerMountFolder();
fextl::string GetServerSocketName();
fextl::string GetServerSocketPath();
int GetServerFD();

bool SetupClient(char* InterpreterPath);
Expand Down
8 changes: 7 additions & 1 deletion Source/Tools/FEXServer/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,13 @@ int main(int argc, char** argv, char** const envp) {
return -1;
}

if (!ProcessPipe::InitializeServerSocket()) {
if (!ProcessPipe::InitializeServerSocket(true)) {
// Couldn't create server socket for some reason
PipeScanner::ClosePipes();
return -1;
}

if (!ProcessPipe::InitializeServerSocket(false)) {
// Couldn't create server socket for some reason
PipeScanner::ClosePipes();
return -1;
Expand Down
48 changes: 34 additions & 14 deletions Source/Tools/FEXServer/ProcessPipe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ namespace ProcessPipe {
constexpr int USER_PERMS = S_IRWXU | S_IRWXG | S_IRWXO;
int ServerLockFD {-1};
int ServerSocketFD {-1};
int ServerFSSocketFD {-1};
std::atomic<bool> ShouldShutdown {false};
time_t RequestTimeout {10};
bool Foreground {false};
Expand Down Expand Up @@ -175,40 +176,58 @@ bool InitializeServerPipe() {
return true;
}

bool InitializeServerSocket() {
auto ServerSocketName = FEXServerClient::GetServerSocketName();
bool InitializeServerSocket(bool abstract) {

// Create the initial unix socket
ServerSocketFD = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (ServerSocketFD == -1) {
int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (fd == -1) {
LogMan::Msg::EFmt("Couldn't create AF_UNIX socket: {} {}\n", errno, strerror(errno));
return false;
}

struct sockaddr_un addr {};
addr.sun_family = AF_UNIX;
size_t SizeOfSocketString = std::min(ServerSocketName.size() + 1, sizeof(addr.sun_path) - 1);
addr.sun_path[0] = 0; // Abstract AF_UNIX sockets start with \0
strncpy(addr.sun_path + 1, ServerSocketName.data(), SizeOfSocketString);

size_t SizeOfSocketString;
if (abstract) {
auto ServerSocketName = FEXServerClient::GetServerSocketName();
SizeOfSocketString = std::min(ServerSocketName.size() + 1, sizeof(addr.sun_path) - 1);
addr.sun_path[0] = 0; // Abstract AF_UNIX sockets start with \0
strncpy(addr.sun_path + 1, ServerSocketName.data(), SizeOfSocketString);
} else {
auto ServerSocketPath = FEXServerClient::GetServerSocketPath();
// Unlink the socket file if it exists
// We are being asked to create a daemon, not error check
// We don't care if this failed or not
unlink(ServerSocketPath.c_str());

SizeOfSocketString = std::min(ServerSocketPath.size(), sizeof(addr.sun_path) - 1);
strncpy(addr.sun_path, ServerSocketPath.data(), SizeOfSocketString);
}
// Include final null character.
size_t SizeOfAddr = sizeof(addr.sun_family) + SizeOfSocketString;

// Bind the socket to the path
int Result = bind(ServerSocketFD, reinterpret_cast<struct sockaddr*>(&addr), SizeOfAddr);
int Result = bind(fd, reinterpret_cast<struct sockaddr*>(&addr), SizeOfAddr);
if (Result == -1) {
LogMan::Msg::EFmt("Couldn't bind AF_UNIX socket '{}': {} {}\n", addr.sun_path, errno, strerror(errno));
close(ServerSocketFD);
ServerSocketFD = -1;
close(fd);
return false;
}

listen(ServerSocketFD, 16);
listen(fd, 16);
PollFDs.emplace_back(pollfd {
.fd = ServerSocketFD,
.fd = fd,
.events = POLLIN,
.revents = 0,
});

if (abstract) {
ServerSocketFD = fd;
} else {
ServerFSSocketFD = fd;
}

return true;
}

Expand Down Expand Up @@ -422,6 +441,7 @@ void CloseConnections() {

// Close the server socket so no more connections can be started
close(ServerSocketFD);
close(ServerFSSocketFD);
}

void WaitForRequests() {
Expand All @@ -441,12 +461,12 @@ void WaitForRequests() {
bool Erase {};

if (Event.revents != 0) {
if (Event.fd == ServerSocketFD) {
if (Event.fd == ServerSocketFD || Event.fd == ServerFSSocketFD) {
if (Event.revents & POLLIN) {
// If it is the listen socket then we have a new connection
struct sockaddr_storage Addr {};
socklen_t AddrSize {};
int NewFD = accept(ServerSocketFD, reinterpret_cast<struct sockaddr*>(&Addr), &AddrSize);
int NewFD = accept(Event.fd, reinterpret_cast<struct sockaddr*>(&Addr), &AddrSize);

// Add the new client to the temporary array
NewPollFDs.emplace_back(pollfd {
Expand Down
2 changes: 1 addition & 1 deletion Source/Tools/FEXServer/ProcessPipe.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace ProcessPipe {
bool InitializeServerPipe();
bool InitializeServerSocket();
bool InitializeServerSocket(bool abstract);
void WaitForRequests();
void SetConfiguration(bool Foreground, uint32_t PersistentTimeout);
void Shutdown();
Expand Down
3 changes: 3 additions & 0 deletions docs/ProgrammingConcerns.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ Use `FHU::Filesystem::GetFilename` instead.
#### std::filesystem::copy_file
Use `FHU::Filesystem::CopyFile` instead.

#### std::filesystem::temp_directory_path
See `GetTempFolder()` in `FEXServerClient.cpp` (split/move to `FHU::Filesystem` if needed by other users).

### `std::fstream`
This API always allocates memory and should be avoided.
Use a combination of open and fextl::string APIs instead of fstream.
Expand Down
Loading