/* SPDX-License-Identifier: GPL-2.0 */ #include #include #include #include #include #include #include #include "minecproxy.h" #include "uring.h" #include "server.h" #include "idle.h" #include "ptimer.h" #include "shared/mc-protocol.h" struct idle { struct ptimer_task ptask; struct uring_task task; }; static int idle_check_handshake_complete(struct uring_task *task, _unused_ int res) { int r; assert_return(task, -EINVAL); assert_task_alive_or(DBG_IDLE, task, return -EINTR); r = mc_is_handshake_complete(task->tbuf->buf, task->tbuf->len); debug(DBG_IDLE, "mc_is_handshake_complete returned %i", r); return r; } static void idle_check_handshake_reply(struct uring_task *task, int res) { struct server *server = container_of(task, struct server, idle_task); unsigned online, max; int player_count = -1; assert_return(task); assert_task_alive(DBG_IDLE, task); debug(DBG_IDLE, "res: %i", res); if (res < 0) goto out; if (mc_protocol_parse_status_reply(server->idle_buf.buf, server->idle_buf.len, &online, &max)) player_count = online; else error("failed to parse reply"); out: uring_task_close_fd(task); server_update_active_players(server, player_count); return; } static void idle_check_handshake_sent(struct uring_task *task, int res) { assert_return(task); assert_task_alive(DBG_IDLE, task); debug(DBG_IDLE, "sent %i bytes", res); if (res < 0) { uring_task_close_fd(task); return; } uring_tbuf_read_until(task, idle_check_handshake_complete, idle_check_handshake_reply); } void idle_check_get_player_count(struct server *server, struct connection *conn) { assert_return(server && conn && server->idle_task.priv); if (!mc_protocol_create_status_request( server->idle_buf.buf, sizeof(server->idle_buf.buf), &server->idle_buf.len, &conn->remote)) { error("failed to create mc request"); /* FIXME: is this enough? */ return; } debug(DBG_IDLE, "sending MC message (%zu bytes)", server->idle_buf.len); uring_tbuf_write(&server->idle_task, idle_check_handshake_sent); } static void idle_cb(struct ptimer_task *ptask) { struct idle *idle = container_of(ptask, struct idle, ptask); struct server *server; assert_return(ptask); assert_task_alive(DBG_IDLE, &idle->task); debug(DBG_IDLE, "timer fired"); list_for_each_entry(server, &cfg->servers, list) server_idle_check(server); } static void idle_free(struct uring_task *task) { struct idle *idle = container_of(task, struct idle, task); assert_return(task); debug(DBG_IDLE, "task %p, idle %p", task, idle); xfree(idle); } void idle_refdump() { assert_return_silent(cfg->idle); uring_task_refdump(&cfg->idle->task); } void idle_delete() { assert_return(cfg->idle); debug(DBG_IDLE, "closing fd %i", cfg->idle->task.fd); ptimer_del_task(&cfg->idle->ptask); uring_task_destroy(&cfg->idle->task); cfg->idle = NULL; } void idle_init() { struct idle *idle; assert_return(!cfg->idle); idle = zmalloc(sizeof(*idle)); if (!idle) die("malloc: %m"); ptask_init(&idle->ptask, 60, 0, idle_cb); uring_task_init(&idle->task, "idle", uring_parent(), idle_free); ptimer_add_task(&idle->ptask); cfg->idle = idle; }