summaryrefslogtreecommitdiff
path: root/announce.c
blob: 9578b0c2e177789372fe2585de6b73d902455619 (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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#include <inttypes.h>
#include <sys/timerfd.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>

#include "main.h"
#include "uring.h"
#include "config.h"
#include "announce.h"
#include "server.h"

struct announce {
	uint64_t value;
	struct uring_task task;
	struct uring_task mcast_task;
};

static void
mcast_free(struct uring_task *task)
{
	struct announce *aev = container_of(task, struct announce, mcast_task);

	debug(DBG_ANN, "task %p, aev %p\n", task, aev);
}

static void
mcast_sent(struct cfg *cfg, struct uring_task *task, int res)
{
	if (res < 0)
		error("failure %i\n", res);
	else
		debug(DBG_ANN, "result %i\n", res);
}

static void
mcast_send(struct cfg *cfg, struct announce *aev, struct server *server)
{
	int len;

	if (!server || !server->pretty_name || server->announce_port < 1)
		return;

	len = snprintf(server->mcast_buf.buf, sizeof(server->mcast_buf.buf),
		       "[MOTD]%s[/MOTD][AD]%" PRIu16 "[/AD]",
		       server->pretty_name, server->announce_port);

	if (len < 1 || len >= sizeof(server->mcast_buf.buf)) {
		error("snprintf returned %i\n", len);
		return;
	}

	server->mcast_buf.len = len;
	uring_task_set_buf(&aev->mcast_task, &server->mcast_buf);
	uring_tbuf_sendmsg(cfg, &aev->mcast_task, mcast_sent);
}

static void
mcast_send_all(struct cfg *cfg, struct announce *aev)
{
	struct server *server;

	list_for_each_entry(server, &cfg->servers, list) {
		verbose("Announcing server: %s\n", server->name);
		mcast_send(cfg, aev, server);
	}
}

static void
announce_cb(struct cfg *cfg, struct uring_task *task, int res)
{
	struct announce *aev = container_of(task, struct announce, task);

	debug(DBG_ANN, "res %i\n", res);

	if (task->dead) {
		debug(DBG_ANN, "task is dead\n");
		return;
	}

	if (res != sizeof(aev->value))
		perrordie("timerfd_read");

	debug(DBG_ANN, "timerfd value %" PRIu64 "\n", aev->value);
	mcast_send_all(cfg, aev);
	uring_read(cfg, &aev->task, &aev->value, sizeof(aev->value), announce_cb);
}

static void
announce_free(struct uring_task *task)
{
	struct announce *aev = container_of(task, struct announce, task);

	debug(DBG_ANN, "task %p, aev 0x%p\n", task, aev);
	xfree(aev);
}

void
announce_refdump(struct announce *aev)
{
	if (!aev)
		return;

	uring_task_refdump(&aev->task);
	uring_task_refdump(&aev->mcast_task);
}

void
announce_delete(struct cfg *cfg)
{
	if (!cfg->aev) {
		error("missing parameters\n");
		return;
	}

	debug(DBG_ANN, "closing fd %i\n", cfg->aev->task.fd);
	uring_task_destroy(cfg, &cfg->aev->mcast_task);
	uring_task_destroy(cfg, &cfg->aev->task);
	cfg->aev = NULL;
}

void
announce_stop(struct announce *aev)
{
	struct itimerspec tspec = {
		.it_interval = {
			.tv_sec  = 0,
			.tv_nsec = 0
		},
		.it_value = {
			.tv_sec  = 0,
			.tv_nsec = 0
		}
	};

	if (timerfd_settime(aev->task.fd, 0, &tspec, NULL) != 0)
		perrordie("timerfd_settime");
}

void
announce_start(struct announce *aev)
{
	struct itimerspec tspec = {
		.it_interval = {
			.tv_sec  = 3,
			.tv_nsec = 0
		},
		.it_value = {
			.tv_sec  = 3,
			.tv_nsec = 0
		}
	};

	if (timerfd_settime(aev->task.fd, 0, &tspec, NULL) != 0)
		perrordie("timerfd_settime");
}

void
announce_init(struct cfg *cfg)
{
	struct announce *aev;
	int afd;
	int sfd;
 
	aev = zmalloc(sizeof(*aev));
	if (!aev)
		perrordie("malloc");

	afd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
	if (afd < 0)
		perrordie("timerfd_create");

	sfd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
	if (sfd < 0)
		perrordie("socket");

	uring_task_init(&aev->task, "aev", uring_parent(cfg), announce_free);
	uring_task_set_fd(&aev->task, afd);

	uring_task_init(&aev->mcast_task, "aev_mcast", &aev->task, mcast_free);
	uring_task_set_fd(&aev->mcast_task, sfd);
	aev->mcast_task.addr.in4.sin_family = AF_INET;
	aev->mcast_task.addr.in4.sin_addr.s_addr = inet_addr("224.0.2.60");
	aev->mcast_task.addr.in4.sin_port = htons(4445);
	aev->mcast_task.addr.addrlen = sizeof(aev->mcast_task.addr.in4);

	cfg->aev = aev;
	uring_read(cfg, &aev->task, &aev->value, sizeof(aev->value), announce_cb);
}