server.c (12233B)
1 #include "hex_server.h" 2 3 bool 4 server_init(struct server_state *state) 5 { 6 assert(state); 7 8 struct addrinfo hints = { 9 .ai_family = AF_UNSPEC, 10 .ai_socktype = SOCK_STREAM, 11 .ai_flags = AI_PASSIVE, 12 }, *addrinfo, *ptr; 13 14 int res; 15 if ((res = getaddrinfo("localhost", "0", &hints, &addrinfo))) { 16 errlog("[server] Failed to get address information: %s\n", gai_strerror(res)); 17 goto error_without_socket; 18 } 19 20 for (ptr = addrinfo; ptr; ptr = ptr->ai_next) { 21 state->servfd = socket(ptr->ai_family, ptr->ai_socktype | SOCK_CLOEXEC, ptr->ai_protocol); 22 if (state->servfd == -1) continue; 23 if (bind(state->servfd, ptr->ai_addr, ptr->ai_addrlen) != -1) break; 24 close(state->servfd); 25 } 26 27 freeaddrinfo(addrinfo); 28 29 if (!ptr) { 30 errlog("[server] Failed to bind server socket\n"); 31 goto error_without_socket; 32 } 33 34 state->serv_addrlen = sizeof state->serv_addr; 35 if (getsockname(state->servfd, (struct sockaddr *) &state->serv_addr, &state->serv_addrlen)) { 36 errlog("[server] Failed to get server socket addr\n"); 37 goto error; 38 } 39 40 if ((res = getnameinfo((struct sockaddr *) &state->serv_addr, state->serv_addrlen, 41 state->serv_host, sizeof state->serv_host, 42 state->serv_port, sizeof state->serv_port, 43 NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { 44 errlog("[server] Failed to get bound socket addr host and port\n"); 45 goto error; 46 } 47 48 listen(state->servfd, 2); 49 50 dbglog("[server] Server socket is listening on %s:%s\n", state->serv_host, state->serv_port); 51 52 return true; 53 54 error: 55 close(state->servfd); 56 57 error_without_socket: 58 return false; 59 } 60 61 void 62 server_free(struct server_state *state) 63 { 64 assert(state); 65 66 close(state->servfd); 67 } 68 69 bool 70 server_spawn_agent(struct server_state *state, struct agent_state *agent_state) 71 { 72 assert(state); 73 assert(agent_state); 74 75 int fd; 76 if ((fd = mkstemp(agent_state->logfile)) != -1) { 77 fchmod(fd, HEX_AGENT_LOGFILE_MODE); 78 79 dbglog("[server] Created logfile '%s' for agent: '%s'\n", 80 agent_state->logfile, agent_state->agent); 81 } else { 82 dbglog("[server] Failed to create logfile '%s' for agent: '%s'\n", 83 agent_state->logfile, agent_state->agent); 84 85 strcpy(agent_state->logfile, "/dev/null"); 86 } 87 88 pid_t child_pid = fork(); 89 90 if (child_pid == 0) { /* child process, exec() agent */ 91 pid_t pid = getpid(); 92 93 dbglog("[server] Child process '%" PRIi32 "', setting uid\n", pid); 94 95 if (setuid(agent_state->agent_uid) == -1) { 96 perror("setuid"); 97 exit(EXIT_FAILURE); /* fork()-d process can die without issue */ 98 } 99 100 dbglog("[server] Child process '%" PRIi32 "', setting resource limits\n", pid); 101 102 struct rlimit limit; 103 104 limit.rlim_cur = limit.rlim_max = opts.agent_threads; 105 prlimit(pid, RLIMIT_NPROC, &limit, NULL); 106 107 limit.rlim_cur = limit.rlim_max = opts.agent_mem_mib * 1024 * 1024; 108 prlimit(pid, RLIMIT_DATA, &limit, NULL); 109 110 char *args[] = { 111 agent_state->agent, 112 state->serv_host, 113 state->serv_port, 114 NULL, 115 }; 116 117 char *env[] = { 118 NULL, 119 }; 120 121 dbglog("[server] Child process '%" PRIi32 "', exec()-ing agent: '%s'\n", 122 pid, agent_state->agent); 123 124 if (!freopen("/dev/null", "rb", stdin)) { 125 perror("freopen(stdin)"); 126 exit(EXIT_FAILURE); /* fork()-d process can die without issue */ 127 } 128 129 if (!freopen(agent_state->logfile, "wb", stdout)) { 130 perror("freopen(stdout)"); 131 exit(EXIT_FAILURE); /* fork()-d process can die without issue */ 132 } 133 134 if (!freopen(agent_state->logfile, "wb", stderr)) { 135 perror("freopen(stderr)"); 136 exit(EXIT_FAILURE); /* fork()-d process can die without issue */ 137 } 138 139 if (execve(agent_state->agent, args, env)) { 140 perror("execve"); 141 exit(EXIT_FAILURE); /* fork()-d process can die without issue */ 142 } 143 } else if (child_pid == -1) { /* parent process, fork() error */ 144 perror("fork"); 145 errlog("[server] Failed to fork() to agent process: '%s'\n", agent_state->agent); 146 goto error; 147 } 148 149 /* parent process, fork() success */ 150 151 /* accept() the agent socket */ 152 struct pollfd pollfds[] = { 153 { .fd = state->servfd, .events = POLLIN, }, 154 }; 155 156 int ready = poll(pollfds, 1, HEX_AGENT_ACCEPT_TIMEOUT_MS); 157 158 if (ready == -1) { 159 perror("poll"); 160 goto error; 161 } else if (ready == 0) { 162 errlog("[server] %s (%s) timed out during accept() period, assuming forfeit\n", 163 hexplayerstr(agent_state->player), agent_state->agent); 164 goto error; 165 } 166 167 int sockflags = SOCK_CLOEXEC; 168 agent_state->sockfd = accept4(state->servfd, 169 (struct sockaddr *) &agent_state->sock_addr, 170 &agent_state->sock_addrlen, 171 sockflags); 172 173 if (agent_state->sockfd == -1) { 174 perror("accept4"); 175 goto error; 176 } 177 178 return true; 179 180 error: 181 kill(0, SIGKILL); 182 183 int wpid, wstatus; 184 while ((wpid = wait(&wstatus)) > 0); /* wait for all children to die */ 185 186 return false; 187 } 188 189 void 190 server_wait_all_agents(struct server_state *state) 191 { 192 assert(state); 193 194 int wpid, wstatus; 195 while ((wpid = wait(&wstatus)) > 0) { 196 dbglog("[server] Child process '%" PRIi32 "' returned code: %d\n", 197 wpid, WEXITSTATUS(wstatus)); 198 } 199 } 200 201 static enum hex_error 202 send_msg(struct agent_state *agent, struct hex_msg *msg, b32 force); 203 204 static enum hex_error 205 recv_msg(struct agent_state *agent, struct hex_msg *out, enum hex_msg_type *expected, size_t len); 206 207 static enum hex_error 208 play_round(struct server_state *state, size_t turn, enum hex_player *winner); 209 210 void 211 server_run(struct server_state *state, struct statistics *statistics) 212 { 213 assert(state); 214 215 enum hex_error err; 216 217 enum hex_player winner; 218 219 /* setup common statistics */ 220 statistics->agent_1 = state->black_agent.agent; 221 statistics->agent_2 = state->white_agent.agent; 222 223 /* send a start message to both agents, including all game parameters 224 */ 225 struct hex_msg msg; 226 msg.type = HEX_MSG_START; 227 msg.data.start.board_size = opts.board_size; 228 msg.data.start.game_secs = opts.game_secs; 229 msg.data.start.thread_limit = opts.agent_threads; 230 msg.data.start.mem_limit_mib = opts.agent_mem_mib; 231 232 msg.data.start.player = HEX_PLAYER_BLACK; 233 if ((err = send_msg(&state->black_agent, &msg, true))) goto forfeit_black; 234 235 msg.data.start.player = HEX_PLAYER_WHITE; 236 if ((err = send_msg(&state->white_agent, &msg, true))) goto forfeit_white; 237 238 size_t round = 0; 239 while ((err = play_round(state, round++, &winner)) == HEX_ERROR_OK); 240 241 msg.type = HEX_MSG_END; 242 msg.data.end.winner = winner; 243 244 send_msg(&state->black_agent, &msg, true); 245 send_msg(&state->white_agent, &msg, true); 246 247 /* calculate game statistics 248 */ 249 statistics->agent_1_won = state->black_agent.player == winner; 250 statistics->agent_2_won = state->white_agent.player == winner; 251 252 statistics->agent_1_rounds = (round + 1) / 2; 253 statistics->agent_2_rounds = round / 2; 254 255 statistics->agent_1_secs = state->black_agent.timer.tv_sec 256 + state->black_agent.timer.tv_nsec / (f32) NANOSECS; 257 statistics->agent_2_secs = state->white_agent.timer.tv_sec 258 + state->white_agent.timer.tv_nsec / (f32) NANOSECS; 259 260 if (winner == HEX_PLAYER_BLACK) { 261 statistics->agent_1_err = HEX_ERROR_OK; 262 statistics->agent_2_err = err; 263 } else { 264 statistics->agent_1_err = err; 265 statistics->agent_2_err = HEX_ERROR_OK; 266 } 267 268 return; 269 270 forfeit_black: 271 statistics->agent_1_won = false; 272 statistics->agent_2_won = true; 273 274 statistics->agent_1_rounds = statistics->agent_2_rounds = 0; 275 276 statistics->agent_1_secs = state->black_agent.timer.tv_sec 277 + state->black_agent.timer.tv_nsec / (f32) NANOSECS; 278 statistics->agent_2_secs = state->white_agent.timer.tv_sec 279 + state->white_agent.timer.tv_nsec / (f32) NANOSECS; 280 281 return; 282 283 forfeit_white: 284 statistics->agent_1_won = true; 285 statistics->agent_2_won = false; 286 287 statistics->agent_1_rounds = statistics->agent_2_rounds = 0; 288 289 statistics->agent_1_secs = state->black_agent.timer.tv_sec 290 + state->black_agent.timer.tv_nsec / (f32) NANOSECS; 291 statistics->agent_2_secs = state->white_agent.timer.tv_sec 292 + state->white_agent.timer.tv_nsec / (f32) NANOSECS; 293 294 return; 295 } 296 297 static enum hex_error 298 send_msg(struct agent_state *agent, struct hex_msg *msg, b32 force) 299 { 300 assert(agent); 301 assert(msg); 302 303 size_t nbytes_sent = 0; 304 305 u8 buf[HEX_MSG_SZ]; 306 if (!hex_msg_try_serialise(msg, buf)) return HEX_ERROR_BAD_MSG; 307 308 struct pollfd pollfd = { .fd = agent->sockfd, .events = POLLOUT, }; 309 310 struct timespec start, end, diff, temp; 311 if (clock_gettime(CLOCK_MONOTONIC, &start) < 0) { 312 perror("clock_gettime"); 313 return HEX_ERROR_SERVER; 314 } 315 316 int res; 317 while (nbytes_sent < ARRLEN(buf) && (res = ppoll(&pollfd, 1, force ? NULL : &agent->timer, NULL)) > 0) { 318 ssize_t curr = send(pollfd.fd, buf + nbytes_sent, ARRLEN(buf) - nbytes_sent, 0); 319 320 if (curr <= 0) /* connection closed or error */ 321 return HEX_ERROR_DISCONNECT; 322 323 if (clock_gettime(CLOCK_MONOTONIC, &end) < 0) { 324 perror("clock_gettime"); 325 return HEX_ERROR_SERVER; 326 } 327 328 difftimespec(&end, &start, &diff); 329 difftimespec(&agent->timer, &diff, &temp); 330 331 start = end; 332 agent->timer = temp; 333 334 nbytes_sent += curr; 335 } 336 337 if (res == 0) { /* timeout */ 338 dbglog("[server] Timeout when sending message to %s\n", 339 hexplayerstr(agent->player)); 340 return HEX_ERROR_TIMEOUT; 341 } 342 343 if (res == -1) { 344 perror("ppoll"); 345 return HEX_ERROR_SERVER; 346 } 347 348 return HEX_ERROR_OK; 349 } 350 351 static enum hex_error 352 recv_msg(struct agent_state *agent, struct hex_msg *out, enum hex_msg_type *expected, size_t len) 353 { 354 assert(agent); 355 assert(out); 356 assert(expected); 357 358 size_t nbytes_received = 0; 359 360 u8 buf[HEX_MSG_SZ]; 361 362 struct pollfd pollfd = { .fd = agent->sockfd, .events = POLLIN, }; 363 364 struct timespec start, end, diff, temp; 365 if (clock_gettime(CLOCK_MONOTONIC, &start) < 0) { 366 perror("clock_gettime"); 367 return HEX_ERROR_SERVER; 368 } 369 370 int res; 371 while (nbytes_received < ARRLEN(buf) && (res = ppoll(&pollfd, 1, &agent->timer, NULL)) > 0) { 372 ssize_t curr = recv(pollfd.fd, buf + nbytes_received, ARRLEN(buf) - nbytes_received, 0); 373 374 if (curr <= 0) /* connection closed or error */ 375 return HEX_ERROR_DISCONNECT; 376 377 if (clock_gettime(CLOCK_MONOTONIC, &end) < 0) { 378 perror("clock_gettime"); 379 return HEX_ERROR_SERVER; 380 } 381 382 difftimespec(&end, &start, &diff); 383 difftimespec(&agent->timer, &diff, &temp); 384 385 start = end; 386 agent->timer = temp; 387 388 nbytes_received += curr; 389 } 390 391 if (res == 0) { /* timeout */ 392 dbglog("[server] Timeout while receiving message from %s\n", 393 hexplayerstr(agent->player)); 394 return HEX_ERROR_TIMEOUT; 395 } 396 397 if (res == -1) { 398 perror("ppoll"); 399 return HEX_ERROR_SERVER; 400 } 401 402 if (!hex_msg_try_deserialise(buf, out)) return HEX_ERROR_BAD_MSG; 403 404 for (size_t i = 0; i < len; i++) { 405 if (out->type == expected[i]) return HEX_ERROR_OK; 406 } 407 408 return HEX_ERROR_BAD_MSG; 409 } 410 411 static enum hex_error 412 play_round(struct server_state *state, size_t turn, enum hex_player *winner) 413 { 414 assert(state); 415 assert(winner); 416 417 enum hex_error err; 418 419 struct agent_state *agents[] = { 420 [HEX_PLAYER_BLACK] = &state->black_agent, 421 [HEX_PLAYER_WHITE] = &state->white_agent, 422 }; 423 424 struct agent_state *player = agents[turn % 2]; 425 struct agent_state *opponent = agents[(turn + 1) % 2]; 426 427 dbglog("[server] round %zu, to-play: %s, opponent: %s\n", 428 turn, hexplayerstr(player->player), hexplayerstr(opponent->player)); 429 430 /* on the first turn for white (i.e. turn 1 when 0-addressed), white 431 * can respond with either a MSG_MOVE, or a MSG_SWAP, but for all 432 * other turns (for both black and white), only a MSG_MOVE can be 433 * played, thus implementing the swap rule. 434 */ 435 enum hex_msg_type expected_msg_types[] = { HEX_MSG_MOVE, HEX_MSG_SWAP, }; 436 size_t expected_msg_types_len = (turn == 1) ? 2 : 1; 437 438 struct hex_msg msg; 439 440 if ((err = recv_msg(player, &msg, expected_msg_types, expected_msg_types_len))) { 441 *winner = opponent->player; 442 return err; 443 } 444 445 switch (msg.type) { 446 case HEX_MSG_MOVE: 447 dbglog("[server] %s made move (%u,%u)\n", 448 hexplayerstr(player->player), msg.data.move.board_x, msg.data.move.board_y); 449 450 if (!board_play(state->board, player->player, msg.data.move.board_x, msg.data.move.board_y)) { 451 *winner = opponent->player; 452 return HEX_ERROR_BAD_MOVE; 453 } 454 455 if (board_completed(state->board, winner)) { 456 board_print(state->board); 457 return HEX_ERROR_GAME_OVER; 458 } 459 break; 460 461 case HEX_MSG_SWAP: 462 dbglog("[server] %s swapped board\n", hexplayerstr(player->player)); 463 464 board_swap(state->board); 465 break; 466 } 467 468 if ((err = send_msg(opponent, &msg, false))) { 469 *winner = player->player; 470 return err; 471 } 472 473 board_print(state->board); 474 475 return HEX_ERROR_OK; 476 }