diff --git a/src/program/GameLoop.cpp b/src/program/GameLoop.cpp index d76e09a4..ad128dd8 100644 --- a/src/program/GameLoop.cpp +++ b/src/program/GameLoop.cpp @@ -52,6 +52,7 @@ #include // stat #include // waitpid // #include +#include #include GameLoop::GameLoop(Context* c) : movie(MovieFile(c)), context(c) @@ -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; @@ -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) { @@ -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 */ diff --git a/src/program/GameThread.cpp b/src/program/GameThread.cpp index 39e7ab2e..b354575d 100644 --- a/src/program/GameThread.cpp +++ b/src/program/GameThread.cpp @@ -32,7 +32,7 @@ #include // chdir() #include // 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 */ @@ -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 arg_list; diff --git a/src/program/GameThread.h b/src/program/GameThread.h index feda9352..18febbd1 100644 --- a/src/program/GameThread.h +++ b/src/program/GameThread.h @@ -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 diff --git a/src/shared/sockethelpers.cpp b/src/shared/sockethelpers.cpp index 5c4b2195..d33da5c6 100644 --- a/src/shared/sockethelpers.cpp +++ b/src/shared/sockethelpers.cpp @@ -19,7 +19,6 @@ #include "sockethelpers.h" #include -#include #include #include #include @@ -27,6 +26,7 @@ #include #include #include +#include #ifdef SOCKET_LOG #include "lcf.h" @@ -35,8 +35,6 @@ #include #endif -#define SOCKET_FILENAME "/tmp/libTAS.socket" - #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif @@ -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(&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(&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); } @@ -134,9 +72,6 @@ bool initSocketGame(void) exit(-1); } #endif - - close(tmp_fd); - return true; } void closeSocket(void) diff --git a/src/shared/sockethelpers.h b/src/shared/sockethelpers.h index 366ee767..bc4d7292 100644 --- a/src/shared/sockethelpers.h +++ b/src/shared/sockethelpers.h @@ -23,14 +23,11 @@ #include #include -/* Remove the socket file and return error */ -int removeSocket(); - -/* Initiate a socket connection with the game */ -bool initSocketProgram(void); +/* Sets 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);