You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

258 lines
6.2 KiB

  1. #pragma once
  2. #include <assert.h>
  3. #include <stdio.h>
  4. #include <stdbool.h>
  5. #include <stdarg.h>
  6. #include <stdlib.h>
  7. #include <unistd.h>
  8. #include <sys/types.h>
  9. #include <sys/wait.h>
  10. #include <sys/time.h>
  11. #include <sys/resource.h>
  12. #include <signal.h>
  13. #include <time.h>
  14. #include "board.h"
  15. #include "mcp.h"
  16. enum exit_reason {
  17. /* Invalid moves disqualify the player. */
  18. INVALID_MOVE_0 = 1,
  19. INVALID_MOVE_1,
  20. /* Crashes do, too. */
  21. CRASH_0,
  22. CRASH_1,
  23. /* Couldn't execute a player. This is a configuration error. */
  24. EXEC_FAILED
  25. };
  26. /* Time given to players after softlimit has been exhausted. */
  27. //#define CLEANUP_TIME 1 /* seconds */
  28. static struct rlimit cpu_limit = { RLIM_INFINITY, RLIM_INFINITY };
  29. static struct rlimit mem_limit = { RLIM_INFINITY, RLIM_INFINITY };
  30. static bool debug = false;
  31. static struct player {
  32. volatile pid_t pid;
  33. int pipe_from_player;
  34. int pipe_to_player;
  35. volatile bool soft_timeout;
  36. volatile bool hard_timeout;
  37. } player[2];
  38. static struct player * volatile current_player = NULL;
  39. /* Specify seconds = RLIM_INFINITY to disarm timer. */
  40. static void
  41. arm_timer(time_t seconds)
  42. {
  43. struct itimerval t;
  44. t.it_interval.tv_sec = 0;
  45. t.it_interval.tv_usec = 0;
  46. t.it_value.tv_sec = (seconds == (int)RLIM_INFINITY) ? 0 : seconds;
  47. t.it_value.tv_usec = 0;
  48. if (setitimer(ITIMER_REAL, &t, NULL) < 0)
  49. abort();
  50. }
  51. static void
  52. alarm_handler(int signum, siginfo_t *, void *)
  53. {
  54. assert(signum == SIGALRM);
  55. struct player *p = current_player;
  56. assert(p);
  57. if (!p->soft_timeout) {
  58. p->soft_timeout = true;
  59. kill(p->pid, SIGXCPU);
  60. arm_timer(static_cast<time_t>(cpu_limit.rlim_max - cpu_limit.rlim_cur));
  61. } else {
  62. p->hard_timeout = true;
  63. kill(p->pid, SIGKILL);
  64. }
  65. }
  66. static char const *
  67. si_code_str(int si_code)
  68. {
  69. switch(si_code) {
  70. case CLD_EXITED: return "exited";
  71. case CLD_KILLED: return "killed";
  72. case CLD_DUMPED: return "coredumped";
  73. case CLD_TRAPPED: return "trapped";
  74. default: break;
  75. }
  76. return "unknown";
  77. }
  78. static char const *
  79. signal_str(int sig)
  80. {
  81. switch(sig)
  82. {
  83. #define CASE(x) case x: return #x;
  84. CASE(SIGHUP); CASE(SIGINT); CASE(SIGILL); CASE(SIGABRT);
  85. CASE(SIGSEGV); CASE(SIGFPE); CASE(SIGPIPE); CASE(SIGKILL);
  86. CASE(SIGTERM); CASE(SIGALRM); CASE(SIGUSR1); CASE(SIGUSR2);
  87. CASE(SIGBUS); CASE(SIGCHLD);
  88. #undef CASE
  89. }
  90. return "unknown";
  91. }
  92. static void
  93. child_handler(int signum, siginfo_t *si, void *)
  94. {
  95. /* For any SIGCHLD code other than STOP/CONT, we print additional info:
  96. * si_code contains what happened to the child. If the child was terminated
  97. * with a signal (most common case), then si_status additionally contains
  98. * the signal that caused termination.
  99. */
  100. if ((si->si_code != CLD_STOPPED) &&
  101. (si->si_code != CLD_CONTINUED)) {
  102. // si_status contains the signal that caused the child to terminate
  103. switch(si->si_status) {
  104. case SIGKILL:
  105. case SIGSEGV:
  106. printf("\n\033[31mCHILD signal: %d ", si->si_status);
  107. printf("(%s,%s)\033[0m\n", si_code_str(si->si_code), signal_str(si->si_status));
  108. printf("killing the other one\n");
  109. for (int i = 0; i < 2; i++) {
  110. int status;
  111. kill(player[i].pid, SIGTERM);
  112. kill(player[i].pid, SIGCONT);
  113. waitpid(player[i].pid, &status, 0);
  114. }
  115. printf("MCP exiting\n");
  116. exit(1);
  117. break;
  118. case SIGTERM:
  119. printf("\n\033[32mCHILD terminated successfully\033[0m\n");
  120. break;
  121. default:
  122. printf(" HANDLEME!\n");
  123. break;
  124. }
  125. }
  126. }
  127. static void __attribute__((noreturn))
  128. exit_msg(enum exit_reason r, const char *msg, ...)
  129. {
  130. va_list ap;
  131. va_start(ap, msg);
  132. vfprintf(stderr, msg, ap);
  133. exit(r);
  134. va_end(ap);
  135. }
  136. static bool
  137. player_move(struct player *the_player,
  138. const game_state *state,
  139. game_move *move)
  140. {
  141. bool succ;
  142. the_player->hard_timeout = false;
  143. the_player->soft_timeout = false;
  144. /* Arm timer with softlimit */
  145. current_player = the_player;
  146. arm_timer(static_cast<time_t>(cpu_limit.rlim_cur));
  147. if (kill(the_player->pid, SIGCONT) < 0) return false;
  148. ssize_t written = serialize_state(the_player->pipe_to_player, state);
  149. if (the_player->hard_timeout) goto timeout;
  150. if (written < 0)
  151. return false;
  152. succ = deserialize_move(the_player->pipe_from_player, move);
  153. if (the_player->hard_timeout) goto timeout;
  154. if (!succ) return false;
  155. /* Stop player and disarm timer. */
  156. if (kill(the_player->pid, SIGSTOP) < 0) return false;
  157. arm_timer(static_cast<time_t>(RLIM_INFINITY));
  158. current_player = NULL;
  159. return true;
  160. timeout:
  161. fprintf(stderr, "Player timeout!\n");
  162. return false;
  163. }
  164. static void
  165. print_usage()
  166. {
  167. fprintf(stderr, "Usage: mcp [-s state_string] [-t soft-player-time] [-m soft-player-mem]\n"
  168. " [-T hard-player-time] [-M hard-player-mem]\n"
  169. " [-d]\n"
  170. " player1 player2\n"
  171. " state_string - 65 character string representing an initial field state\n"
  172. " (this is the string the MCP prints before every move.)\n"
  173. " player-time - CPU time per turn in seconds\n"
  174. " player-mem - Memory limit per player in megabytes\n"
  175. " -d - turn on debugging, program won't exit on invalid move\n");
  176. }
  177. static void
  178. setup_signal_handlers()
  179. {
  180. /* Ignore SIGPIPE. Accessing dead pipes now returns EPIPE. */
  181. struct sigaction pact;
  182. pact.sa_handler = SIG_IGN;
  183. sigemptyset(&pact.sa_mask);
  184. pact.sa_restorer = 0;
  185. pact.sa_sigaction = 0;
  186. pact.sa_flags = 0;
  187. if (sigaction(SIGPIPE, &pact, NULL) != 0)
  188. abort();
  189. /* Handle SIGALRM. */
  190. struct sigaction aact;
  191. aact.sa_handler = 0;
  192. sigemptyset(&aact.sa_mask);
  193. aact.sa_restorer = 0;
  194. aact.sa_sigaction = alarm_handler;
  195. aact.sa_flags = SA_SIGINFO | SA_RESTART;
  196. if (sigaction(SIGALRM, &aact, NULL) != 0)
  197. abort();
  198. // Catch SIGCHLD
  199. struct sigaction cact;
  200. cact.sa_handler = 0;
  201. sigemptyset(&cact.sa_mask);
  202. cact.sa_restorer = 0;
  203. cact.sa_sigaction = child_handler;
  204. cact.sa_flags = SA_SIGINFO | SA_RESTART /*| SA_NOCLDSTOP*/;
  205. if (sigaction(SIGCHLD, &cact, NULL) != 0)
  206. abort();
  207. }
  208. static void getTimeStr(char *timebuf, size_t len)
  209. {
  210. time_t rawtime;
  211. struct tm *timeinfo;
  212. time(&rawtime);
  213. if (rawtime == (time_t)-1) {
  214. perror("time()");
  215. }
  216. timeinfo = localtime(&rawtime);
  217. if (!timeinfo) {
  218. perror("localtime()");
  219. }
  220. strftime(timebuf, len, "%H:%M:%S", timeinfo);
  221. }