Skip to content

Commit

Permalink
Use socketpair() to create sockets
Browse files Browse the repository at this point in the history
  • Loading branch information
jakobrs committed Jul 9, 2021
1 parent 2ae7dd5 commit c97adc6
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 92 deletions.
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);
/* 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);
Expand Down

0 comments on commit c97adc6

Please sign in to comment.