#include #include #include #include #include #include #include #include #include "fscomch.h" cancelToken_t init_cancel_token_timeout(int seconds) { cancelToken_t token; token.timeout = seconds; token.value = calloc(1, sizeof(char)); pthread_t cancelTimer; cancelToken_t* memToken = malloc(sizeof(cancelToken_t)); *memToken = token; pthread_create(&cancelTimer, NULL, cancel_in_secs, (void*)memToken); return token; } cancelToken_t init_cancel_token_never() { cancelToken_t token; token.timeout = -1; token.value = NULL; return token; } match_t init_match(char* uuid, FILE* streamIn, FILE* streamOut) { match_t match; match.uuid = uuid; match.streamIn = streamIn; match.streamOut = streamOut; return match; } void destroy_match(match_t match) { free(match.uuid); fclose(match.streamIn); fclose(match.streamOut); } FILE* try_lock_match() { mkdir("/tmp/chess/", 0777); mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; int fd = open("/tmp/chess/match", O_RDONLY | O_CREAT | O_EXCL, mode); if (fd == -1) { return NULL; } return fdopen(fd, "r"); } match_t await_match(FILE* matchFile, cancelToken_t cancelToken) { /* await match UUID */ char* uuid = calloc(37, sizeof(char)); while (cancelToken.value == NULL || !*cancelToken.value) { if (fgets(uuid, 37, matchFile) != NULL) { break; } fseek(matchFile, 0, SEEK_SET); struct timespec req; req.tv_sec = 0; req.tv_nsec = awaitMatchNs; nanosleep(&req, NULL); } /* connect to streams */ char aName[256]; char bName[256]; snprintf(aName, 256, "/tmp/chess/%s.a", uuid); snprintf(bName, 256, "/tmp/chess/%s.b", uuid); FILE* streamIn = fopen(aName, "r"); FILE* streamOut = fopen(bName, "a"); /* connection complete, delete the match file and the join lock */ remove("/tmp/chess/match"); remove("/tmp/chess/matchjoin"); match_t match = init_match(uuid, streamIn, streamOut); send_message("ping", match); return match; } match_t join_match() { /* create a join lock so that no one else can join this match */ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; int fd = open("/tmp/chess/matchjoin", O_WRONLY | O_CREAT | O_EXCL, mode); if (fd == -1) { /* another process is in the process of joining this game */ match_t match; return match; } close(fd); /* this is definitely the only process joining the game proceed to generate a uuid */ char* uuid = make_uuid(); /* create and connect to streams */ char aName[256]; char bName[256]; snprintf(aName, 256, "/tmp/chess/%s.a", uuid); snprintf(bName, 256, "/tmp/chess/%s.b", uuid); FILE* streamIn = fopen(bName, "w+"); FILE* streamOut = fopen(aName, "a"); match_t match = init_match(uuid, streamIn, streamOut); /* send uuid to the host */ FILE* matchFile = fopen("/tmp/chess/match", "a"); fputs(uuid, matchFile); fclose(matchFile); /* handshake */ cancelToken_t token = init_cancel_token_timeout(2); char* message = read_message(match, token); if (message != NULL) { if (strcmp(message, "ping") == 0) { return match; } else { printf("Extreme de-sync. Aborting\n"); exit(1); } } printf("Host is dead. Cleaning up\n"); fclose(streamIn); fclose(streamOut); remove("/tmp/chess/match"); remove("/tmp/chess/matchjoin"); remove(aName); remove(bName); exit(1); } void send_message(char* content, match_t match) { fprintf(match.streamOut, "%s\n", content); fflush(match.streamOut); } char* read_message(match_t match, cancelToken_t cancelToken) { char* buffer = calloc(256, sizeof(char)); while (cancelToken.value == NULL || !*cancelToken.value) { if (fgets(buffer, 256, match.streamIn) != NULL) { break; } /* wtf? */ fseek(match.streamIn, ftell(match.streamIn), SEEK_SET); struct timespec req; req.tv_sec = 0; req.tv_nsec = awaitMoveNs; nanosleep(&req, NULL); } /* if we cancelled via token, return NULL */ if (cancelToken.value != NULL && *cancelToken.value == 1) { free(buffer); return NULL; } /* overwrite potential newline */ buffer[strlen(buffer) - 1] = '\0'; return buffer; } char* make_uuid() { char sym[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f'}; char* out = calloc(37, sizeof(char)); srand(time(NULL)); for (int i = 0; i < 36; i++) { if (i == 8 || i == 13 || i == 18 || i == 23) { out[i] = '-'; } else { out[i] = sym[rand() % 16]; } } out[37] = '\0'; return out; } void* cancel_in_secs(void* token) { sleep(((cancelToken_t*)token)->timeout); *(((cancelToken_t*)token)->value) = 1; free(token); }