#include #include #include #include #include #include #include "main.h" #include "uring.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); assert_return(task); debug(DBG_ANN, "task %p, aev %p", task, aev); } static void mcast_sent(struct cfg *cfg, struct uring_task *task, int res) { struct server *server = container_of(task->tbuf, struct server, mcast_buf); assert_return(task && task->tbuf); if (res < 0) error("failure %i", res); else debug(DBG_ANN, "result %i", res); uring_task_put(cfg, &server->task); } static void mcast_send(struct cfg *cfg, struct announce *aev, struct server *server) { int len; assert_return(cfg && aev && server); /* FIXME: should these be assert:ed as well? */ if (!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", len); return; } server->mcast_buf.len = len; uring_task_set_buf(&aev->mcast_task, &server->mcast_buf); uring_task_get(cfg, &server->task); uring_tbuf_sendmsg(cfg, &aev->mcast_task, mcast_sent); } static void mcast_send_all(struct cfg *cfg, struct announce *aev) { struct server *server; assert_return(cfg && aev); list_for_each_entry(server, &cfg->servers, list) { verbose("Announcing server: %s", 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); assert_return(cfg && task); assert_task_alive(DBG_ANN, task); if (res != sizeof(aev->value)) { error("timerfd_read: %m"); return; } debug(DBG_ANN, "timerfd value %" PRIu64, 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); assert_return(task); debug(DBG_ANN, "task %p, aev 0x%p", task, aev); xfree(aev); } void announce_refdump(struct announce *aev) { assert_return(aev); uring_task_refdump(&aev->task); uring_task_refdump(&aev->mcast_task); } void announce_delete(struct cfg *cfg) { assert_return(cfg && cfg->aev); debug(DBG_ANN, "closing fd %i", 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 } }; assert_return(aev); if (timerfd_settime(aev->task.fd, 0, &tspec, NULL) != 0) error("timerfd_settime: %m"); } 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 } }; assert_return(aev); if (timerfd_settime(aev->task.fd, 0, &tspec, NULL) != 0) error("timerfd_settime: %m"); } void announce_init(struct cfg *cfg) { struct announce *aev; int afd; int sfd; assert_return(cfg); aev = zmalloc(sizeof(*aev)); if (!aev) die("malloc: %m"); afd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); if (afd < 0) die("timerfd_create: %m"); sfd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sfd < 0) die("socket: %m"); 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); saddr_set_ipv4(&aev->mcast_task.saddr, cinet_addr(224,0,2,60), htons(4445)); cfg->aev = aev; uring_read(cfg, &aev->task, &aev->value, sizeof(aev->value), announce_cb); }