#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include "board.h" #include "mcp.h" enum exit_reason { /* Invalid moves disqualify the player. */ INVALID_MOVE_0 = 1, INVALID_MOVE_1, /* Crashes do, too. */ CRASH_0, CRASH_1, /* Couldn't execute a player. This is a configuration error. */ EXEC_FAILED }; /* Time given to players after softlimit has been exhausted. */ //#define CLEANUP_TIME 1 /* seconds */ static struct rlimit cpu_limit = { RLIM_INFINITY, RLIM_INFINITY }; static struct rlimit mem_limit = { RLIM_INFINITY, RLIM_INFINITY }; static bool debug = false; static struct player { volatile pid_t pid; int pipe_from_player; int pipe_to_player; volatile bool soft_timeout; volatile bool hard_timeout; } player[2]; static struct player * volatile current_player = NULL; /* Specify seconds = RLIM_INFINITY to disarm timer. */ static void arm_timer(time_t seconds) { struct itimerval t; t.it_interval.tv_sec = 0; t.it_interval.tv_usec = 0; t.it_value.tv_sec = (seconds == (int)RLIM_INFINITY) ? 0 : seconds; t.it_value.tv_usec = 0; if (setitimer(ITIMER_REAL, &t, NULL) < 0) abort(); } static void alarm_handler(int signum, siginfo_t *, void *) { assert(signum == SIGALRM); struct player *p = current_player; assert(p); if (!p->soft_timeout) { p->soft_timeout = true; kill(p->pid, SIGXCPU); arm_timer(static_cast(cpu_limit.rlim_max - cpu_limit.rlim_cur)); } else { p->hard_timeout = true; kill(p->pid, SIGKILL); } } static char const * si_code_str(int si_code) { switch(si_code) { case CLD_EXITED: return "exited"; case CLD_KILLED: return "killed"; case CLD_DUMPED: return "coredumped"; case CLD_TRAPPED: return "trapped"; default: break; } return "unknown"; } static char const * signal_str(int sig) { switch(sig) { #define CASE(x) case x: return #x; CASE(SIGHUP); CASE(SIGINT); CASE(SIGILL); CASE(SIGABRT); CASE(SIGSEGV); CASE(SIGFPE); CASE(SIGPIPE); CASE(SIGKILL); CASE(SIGTERM); CASE(SIGALRM); CASE(SIGUSR1); CASE(SIGUSR2); CASE(SIGBUS); CASE(SIGCHLD); #undef CASE } return "unknown"; } static void child_handler(int signum, siginfo_t *si, void *) { /* For any SIGCHLD code other than STOP/CONT, we print additional info: * si_code contains what happened to the child. If the child was terminated * with a signal (most common case), then si_status additionally contains * the signal that caused termination. */ if ((si->si_code != CLD_STOPPED) && (si->si_code != CLD_CONTINUED)) { // si_status contains the signal that caused the child to terminate switch(si->si_status) { case SIGKILL: case SIGSEGV: printf("\n\033[31mCHILD signal: %d ", si->si_status); printf("(%s,%s)\033[0m\n", si_code_str(si->si_code), signal_str(si->si_status)); printf("killing the other one\n"); for (int i = 0; i < 2; i++) { int status; kill(player[i].pid, SIGTERM); kill(player[i].pid, SIGCONT); waitpid(player[i].pid, &status, 0); } printf("MCP exiting\n"); exit(1); break; case SIGTERM: printf("\n\033[32mCHILD terminated successfully\033[0m\n"); break; default: printf(" HANDLEME!\n"); break; } } } static void __attribute__((noreturn)) exit_msg(enum exit_reason r, const char *msg, ...) { va_list ap; va_start(ap, msg); vfprintf(stderr, msg, ap); exit(r); va_end(ap); } static bool player_move(struct player *the_player, const game_state *state, game_move *move) { bool succ; the_player->hard_timeout = false; the_player->soft_timeout = false; /* Arm timer with softlimit */ current_player = the_player; arm_timer(static_cast(cpu_limit.rlim_cur)); if (kill(the_player->pid, SIGCONT) < 0) return false; ssize_t written = serialize_state(the_player->pipe_to_player, state); if (the_player->hard_timeout) goto timeout; if (written < 0) return false; succ = deserialize_move(the_player->pipe_from_player, move); if (the_player->hard_timeout) goto timeout; if (!succ) return false; /* Stop player and disarm timer. */ if (kill(the_player->pid, SIGSTOP) < 0) return false; arm_timer(static_cast(RLIM_INFINITY)); current_player = NULL; return true; timeout: fprintf(stderr, "Player timeout!\n"); return false; } static void print_usage() { fprintf(stderr, "Usage: mcp [-s state_string] [-t soft-player-time] [-m soft-player-mem]\n" " [-T hard-player-time] [-M hard-player-mem]\n" " [-d]\n" " player1 player2\n" " state_string - 65 character string representing an initial field state\n" " (this is the string the MCP prints before every move.)\n" " player-time - CPU time per turn in seconds\n" " player-mem - Memory limit per player in megabytes\n" " -d - turn on debugging, program won't exit on invalid move\n"); } static void setup_signal_handlers() { /* Ignore SIGPIPE. Accessing dead pipes now returns EPIPE. */ struct sigaction pact; pact.sa_handler = SIG_IGN; sigemptyset(&pact.sa_mask); pact.sa_restorer = 0; pact.sa_sigaction = 0; pact.sa_flags = 0; if (sigaction(SIGPIPE, &pact, NULL) != 0) abort(); /* Handle SIGALRM. */ struct sigaction aact; aact.sa_handler = 0; sigemptyset(&aact.sa_mask); aact.sa_restorer = 0; aact.sa_sigaction = alarm_handler; aact.sa_flags = SA_SIGINFO | SA_RESTART; if (sigaction(SIGALRM, &aact, NULL) != 0) abort(); // Catch SIGCHLD struct sigaction cact; cact.sa_handler = 0; sigemptyset(&cact.sa_mask); cact.sa_restorer = 0; cact.sa_sigaction = child_handler; cact.sa_flags = SA_SIGINFO | SA_RESTART /*| SA_NOCLDSTOP*/; if (sigaction(SIGCHLD, &cact, NULL) != 0) abort(); } static void getTimeStr(char *timebuf, size_t len) { time_t rawtime; struct tm *timeinfo; time(&rawtime); if (rawtime == (time_t)-1) { perror("time()"); } timeinfo = localtime(&rawtime); if (!timeinfo) { perror("localtime()"); } strftime(timebuf, len, "%H:%M:%S", timeinfo); }