summaryrefslogtreecommitdiff
path: root/minecproxy/idle.c
blob: 8393ff9cc07d0d0391827ead86983fc8a2c2c569 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/* SPDX-License-Identifier: GPL-2.0 */
#include <inttypes.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

#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;
}