#include #include #include #include #include #include #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); 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.saddr.in4.sin_family = AF_INET; aev->mcast_task.saddr.in4.sin_addr.s_addr = inet_addr("224.0.2.60"); aev->mcast_task.saddr.in4.sin_port = htons(4445); aev->mcast_task.saddr.addrlen = sizeof(aev->mcast_task.saddr.in4); saddr_set_str(&aev->mcast_task.saddr); cfg->aev = aev; uring_read(cfg, &aev->task, &aev->value, sizeof(aev->value), announce_cb); }