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

Use socketpair() to create sockets #425

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
26 changes: 16 additions & 10 deletions src/program/GameLoop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
#include <sys/stat.h> // stat
#include <sys/wait.h> // waitpid
// #include <X11/X.h>
#include <sys/socket.h>
#include <stdint.h>

GameLoop::GameLoop(Context* c) : movie(MovieFile(c)), context(c)
Expand Down Expand Up @@ -183,20 +184,29 @@ void GameLoop::init()
/* Remove savestates again in case we did not exist cleanly the previous time */
remove_savestates(context);

/* Remove the file socket */
int err = removeSocket();
if (err != 0)
emit alertToShow(QString("Could not remove socket file /tmp/libTAS.socket: %2").arg(strerror(err)));

/* Init savestate list */
SaveStateList::init(context);

/* Create socket pair */
int sockets[2];

if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1) {
perror("Failed to create socket pair");
return;
}

/* We fork here so that the child process calls the game */
context->fork_pid = fork();
if (context->fork_pid == 0) {
GameThread::launch(context);
close(sockets[0]);

// noreturn; calls exec
return GameThread::launch(context, sockets[1]);
}

close(sockets[1]);
setSocket(sockets[0]);

/* Compute the MD5 hash of the game binary */
context->md5_game.clear();
std::ostringstream cmd;
Expand Down Expand Up @@ -267,9 +277,6 @@ void GameLoop::init()

void GameLoop::initProcessMessages()
{
/* Connect to the socket between the program and the game */
initSocketProgram();

/* Receive informations from the game */
int message = receiveMessage();
while (message != MSGB_END_INIT) {
Expand Down Expand Up @@ -739,7 +746,6 @@ void GameLoop::loopExit()
(context->status != Context::QUITTING))) {

/* We keep the movie opened and indicate the main thread to restart the game */

closeSocket();

/* Remove savestates because they are invalid on future instances of the game */
Expand Down
5 changes: 4 additions & 1 deletion src/program/GameThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
#include <unistd.h> // chdir()
#include <fcntl.h> // O_RDWR, O_CREAT

void GameThread::launch(Context *context)
void GameThread::launch(Context *context, int sock)
{
#ifdef __unix__
/* Update the LD_LIBRARY_PATH environment variable if the user set one */
Expand Down Expand Up @@ -120,6 +120,9 @@ void GameThread::launch(Context *context)
/* Override timezone for determinism */
setenv("TZ", "UTC0", 1);

/* Pass socket file descriptor to the game */
setenv("LIBTAS_SOCKET_FD", std::to_string(sock).c_str(), 1);

/* Build the argument list to be fed to execv */
std::list<std::string> arg_list;

Expand Down
2 changes: 1 addition & 1 deletion src/program/GameThread.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace GameThread {
* Because this function eventually calls execl, it does not return.
* So, it is called from a child process using fork().
*/
void launch(Context *context);
void launch(Context *context, int sock);
}

#endif
83 changes: 9 additions & 74 deletions src/shared/sockethelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@

#include "sockethelpers.h"
#include <sys/socket.h>
#include <sys/stat.h>
#include <cstdlib>
#include <unistd.h>
#include <sys/un.h>
#include <iostream>
#include <vector>
#include <mutex>
#include <errno.h>
#include <fcntl.h>

#ifdef SOCKET_LOG
#include "lcf.h"
Expand All @@ -35,8 +35,6 @@
#include <iostream>
#endif

#define SOCKET_FILENAME "/tmp/libTAS.socket"

#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif
Expand All @@ -48,80 +46,20 @@ static int socket_fd = 0;

static std::mutex mutex;

int removeSocket(void) {
int ret = unlink(SOCKET_FILENAME);
if ((ret == -1) && (errno != ENOENT))
return errno;
return 0;
void setSocket(int sock) {
socket_fd = sock;
}

bool initSocketProgram(void)
void initSocketGame(void)
{
#ifdef __unix__
const struct sockaddr_un addr = { AF_UNIX, SOCKET_FILENAME };
#elif defined(__APPLE__) && defined(__MACH__)
const struct sockaddr_un addr = { sizeof(struct sockaddr_un), AF_UNIX, SOCKET_FILENAME };
#endif
socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);

struct timespec tim = {0, 500L*1000L*1000L};
socket_fd = std::stoi(getenv("LIBTAS_SOCKET_FD"));

const int MAX_RETRIES = 10;
int retry = 0;

nanosleep(&tim, NULL);
while (connect(socket_fd, reinterpret_cast<const struct sockaddr*>(&addr),
sizeof(struct sockaddr_un))) {
std::cout << "Attempt " << retry + 1 << ": Couldn't connect to socket." << std::endl;
retry++;
if (retry < MAX_RETRIES) {
nanosleep(&tim, NULL);
if (fcntl(socket_fd, F_GETFD) == -1) {
if (errno == EBADF) {
perror("LIBTAS_SOCKET_FD has been closed");
} else {
return false;
}
tim.tv_nsec *= 1.5;
if (tim.tv_nsec >= 1000000000) {
tim.tv_sec++;
tim.tv_nsec -= 1000000000;
perror("Unable to use game socket");
}
}
std::cout << "Attempt " << retry + 1 << ": Connected." << std::endl;

return true;
}

bool initSocketGame(void)
{
/* Check if socket file already exists. If so, it is probably because
* the link is already done in another process of the game.
* In this case, we just return immediately.
*/
struct stat st;
int result = stat(SOCKET_FILENAME, &st);
if (result == 0)
return false;

#ifdef __unix__
const struct sockaddr_un addr = { AF_UNIX, SOCKET_FILENAME };
#elif defined(__APPLE__) && defined(__MACH__)
const struct sockaddr_un addr = { sizeof(struct sockaddr_un), AF_UNIX, SOCKET_FILENAME };
#endif
const int tmp_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (bind(tmp_fd, reinterpret_cast<const struct sockaddr*>(&addr), sizeof(struct sockaddr_un)))
{
std::cerr << "Couldn't bind client socket." << std::endl;
exit(-1);
}

if (listen(tmp_fd, 1))
{
std::cerr << "Couldn't listen on client socket." << std::endl;
exit(-1);
}

if ((socket_fd = accept(tmp_fd, NULL, NULL)) < 0)
{
std::cerr << "Couldn't accept client connection." << std::endl;
exit(-1);
}

Expand All @@ -134,9 +72,6 @@ bool initSocketGame(void)
exit(-1);
}
#endif

close(tmp_fd);
return true;
}

void closeSocket(void)
Expand Down
9 changes: 3 additions & 6 deletions src/shared/sockethelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,11 @@
#include <cstddef>
#include <string>

/* Remove the socket file and return error */
int removeSocket();

/* Initiate a socket connection with the game */
bool initSocketProgram(void);
/* Set socket_fd */
void setSocket(int sock);

/* Initiate a socket connection with libTAS */
bool initSocketGame(void);
void initSocketGame(void);

/* Close the socket connection */
void closeSocket(void);
Expand Down