summaryrefslogtreecommitdiff
path: root/server.c
diff options
context:
space:
mode:
Diffstat (limited to 'server.c')
-rw-r--r--server.c837
1 files changed, 0 insertions, 837 deletions
diff --git a/server.c b/server.c
deleted file mode 100644
index de42721..0000000
--- a/server.c
+++ /dev/null
@@ -1,837 +0,0 @@
-#define _GNU_SOURCE
-#include <stdlib.h>
-#include <string.h>
-#include <arpa/inet.h>
-#include <unistd.h>
-#include <inttypes.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sched.h>
-#include <poll.h>
-#include <errno.h>
-#include <sched.h>
-
-#include "main.h"
-#include "uring.h"
-#include "ptimer.h"
-#include "server.h"
-#include "server-proxy.h"
-#include "server-rcon.h"
-#include "utils.h"
-#include "config-parser.h"
-#include "idle.h"
-#include "systemd.h"
-
-static bool
-set_property(struct server *server, char **property, const char *value)
-{
- assert_return(server && !*property && !empty_str(value), false);
-
- *property = xstrdup(value);
- if (!*property) {
- error("strdup: %m");
- return false;
- }
-
- return true;
-}
-
-void
-server_refdump(struct server *server)
-{
- struct server_local *local;
- struct server_proxy *proxy;
-
- assert_return(server);
-
- uring_task_refdump(&server->task);
- uring_task_refdump(&server->exec_task);
- uring_task_refdump(&server->ann_task);
- uring_task_refdump(&server->idle_task);
- list_for_each_entry(local, &server->locals, list)
- local_refdump(local);
- list_for_each_entry(proxy, &server->proxys, list)
- proxy_refdump(proxy);
- rcon_refdump(server);
-}
-
-static void
-server_free(struct uring_task *task)
-{
- struct server *server = container_of(task, struct server, task);
-
- assert_return(task);
-
- debug(DBG_SRV, "freeing server %s (%p)", server->name, server);
- list_del(&server->list);
- xfree(server->pretty_name);
- xfree(server->start_exec);
- xfree(server->stop_exec);
- xfree(server->systemd_service);
- xfree(server->systemd_obj);
- xfree(server->rcon_password);
- xfree(server->name);
- xfree(server);
-}
-
-void
-server_delete(struct server *server)
-{
- struct server_local *local, *ltmp;
- struct server_proxy *proxy, *ptmp;
- struct saddr *remote;
- struct saddr *rcon;
- struct saddr *tmp;
- struct dns_async *dns, *dtmp;
-
- assert_return(server);
-
- verbose("Removing server %s", server->name);
- server->state = SERVER_STATE_DEAD;
-
- rcon_delete(server);
-
- list_for_each_entry_safe(local, ltmp, &server->locals, list)
- local_delete(local);
-
- list_for_each_entry_safe(proxy, ptmp, &server->proxys, list)
- proxy_delete(proxy);
-
- list_for_each_entry_safe(rcon, tmp, &server->rcons, list) {
- list_del(&rcon->list);
- xfree(rcon);
- }
-
- list_for_each_entry_safe(remote, tmp, &server->remotes, list) {
- list_del(&remote->list);
- xfree(remote);
- }
-
- list_for_each_entry_safe(dns, dtmp, &server->dnslookups, list)
- gai_cancel(&dns->gcb);
-
- uring_task_destroy(&server->idle_task);
- uring_poll_cancel(&server->exec_task);
- uring_task_put(&server->exec_task);
- uring_task_destroy(&server->task);
- uring_task_put(&server->ann_task);
-}
-
-void
-server_delete_by_name(const char *name)
-{
- struct server *server;
-
- assert_return(!empty_str(name));
-
- list_for_each_entry(server, &cfg->servers, list) {
- if (streq(server->name, name)) {
- server_delete(server);
- return;
- }
- }
-}
-
-static void
-server_dump(struct server *server)
-{
- struct server_local *local;
- struct saddr *remote;
- struct saddr *rcon;
-
- assert_return(server);
-
- verbose("Server %s:", server->name);
- switch (server->type) {
- case SERVER_TYPE_ANNOUNCE:
- verbose(" * Type: announce");
- break;
- case SERVER_TYPE_PROXY:
- verbose(" * Type: proxy");
- break;
- default:
- verbose(" * Type: unknown");
- break;
- }
- verbose(" * Name: %s", server->pretty_name ? server->pretty_name : "<undefined>");
- verbose(" * Announce port: %" PRIu16, server->announce_port);
-
- if (!list_empty(&server->locals)) {
- verbose(" * Local:");
- list_for_each_entry(local, &server->locals, list)
- verbose(" * %s", local->local.addrstr);
- }
-
- if (!list_empty(&server->remotes)) {
- verbose(" * Remote:");
- list_for_each_entry(remote, &server->remotes, list)
- verbose(" * %s", remote->addrstr);
- }
-
- if (!list_empty(&server->rcons)) {
- verbose(" * RCon:");
- list_for_each_entry(rcon, &server->rcons, list)
- verbose(" * %s", rcon->addrstr);
- }
-
- verbose("");
-}
-
-static void
-server_exec_free(struct uring_task *task)
-{
- assert_return(task);
-
- debug(DBG_SRV, "called");
-}
-
-#ifndef P_PIDFD
-#define P_PIDFD 3
-#endif
-
-/* FIXME: update states */
-static void
-server_exec_done(struct uring_task *task, int res)
-{
- struct server *server = container_of(task, struct server, exec_task);
- int r;
- siginfo_t info;
-
- assert_return(task);
- assert_task_alive_or(DBG_SRV, task, goto out);
- /* Should we leave child processes running? */
-
- if (!(res & POLLIN)) {
- error("unexpected result: %i", res);
- goto out;
- }
-
- r = waitid(P_PIDFD, server->exec_task.fd, &info, WEXITED);
- if (r < 0) {
- error("waitid: %m");
- goto out;
- }
-
- if (info.si_status == 0)
- debug(DBG_SRV, "command successfully executed");
- else
- error("command failed: %i", info.si_status);
-
-out:
- uring_task_close_fd(&server->exec_task);
-}
-
-static int
-server_exec_child(void *ptr)
-{
- const char *cmd = ptr;
-
- assert_return(ptr, EINVAL);
-
- execl(cmd, cmd, NULL);
- return errno;
-}
-
-#ifndef CLONE_PIDFD
-#define CLONE_PIDFD 0x00001000
-#endif
-
-static bool
-server_exec(struct server *server, const char *cmd)
-{
- char stack[4096]; /* Beautiful/horrible hack :) */
- int pidfd;
- int r;
-
- assert_return(server && cmd && server->exec_task.fd < 1, false);
-
- r = clone(server_exec_child, stack + sizeof(stack),
- CLONE_VM | CLONE_VFORK | CLONE_PIDFD | SIGCHLD,
- (void *)cmd, &pidfd);
- if (r < 0) {
- error("clone: %m: %i", r);
- return false;
- }
-
- uring_task_set_fd(&server->exec_task, pidfd);
- uring_poll(&server->exec_task, POLLIN, server_exec_done);
- return true;
-}
-
-static bool
-server_check_running(struct server *server)
-{
- assert_return(server, false);
-
- /* FIXME: other methods, rcon? */
- if (server->systemd_service) {
- verbose("%s: checking if systemd service is running", server->name);
- if (systemd_service_running(server)) {
- server->state = SERVER_STATE_RUNNING;
- return true;
- } else {
- server->state = SERVER_STATE_STOPPED;
- return false;
- }
- }
-
- return false;
-}
-
-bool
-server_start(struct server *server)
-{
- assert_return(server, false);
- assert_task_alive_or(DBG_SRV, &server->task, return false);
-
- switch (server->start_method) {
-
- case SERVER_START_METHOD_EXEC:
- verbose("Starting server %s via external cmd", server->name);
- return server_exec(server, server->start_exec);
-
- case SERVER_START_METHOD_SYSTEMD:
- verbose("Starting server %s via systemd (%s)",
- server->name, server->systemd_service);
-
- if (systemd_service_start(server)) {
- server->state = SERVER_STATE_RUNNING;
- return true;
- } else
- return server_check_running(server);
-
- case SERVER_START_METHOD_UNDEFINED:
- default:
- break;
- }
-
- return false;
-}
-
-bool
-server_stop(struct server *server)
-{
- assert_return(server, false);
- assert_task_alive_or(DBG_SRV, &server->task, return false);
-
- switch (server->stop_method) {
-
- case SERVER_STOP_METHOD_EXEC:
- verbose("Stopping server %s via external cmd", server->name);
- return server_exec(server, server->stop_exec);
-
- case SERVER_STOP_METHOD_SYSTEMD:
- verbose("Stopping server %s via systemd (%s)",
- server->name, server->systemd_service);
- if (systemd_service_stop(server)) {
- server->state = SERVER_STATE_STOPPED;
- return true;
- } else
- return server_check_running(server);
-
- case SERVER_STOP_METHOD_RCON:
- verbose("Stopping server %s via rcon", server->name);
- rcon_stop(server);
- return true;
-
- case SERVER_STOP_METHOD_UNDEFINED:
- default:
- break;
- }
-
- return false;
-}
-
-static void
-server_idle_free(struct uring_task *task)
-{
- assert_return(task);
-
- debug(DBG_ANN, "called");
-}
-
-void
-server_set_active_players(struct server *server, int count)
-{
- assert_return(server);
- assert_task_alive(DBG_IDLE, &server->idle_task);
-
- debug(DBG_IDLE, "%s: currently %i active players",
- server->name, count);
-
- if (count < 0)
- return;
-
- server->state = SERVER_STATE_RUNNING;
- if (count > 0)
- server->idle_count = 0;
- else if (count == 0)
- server->idle_count++;
-
- if (server->idle_count > server->idle_timeout) {
- verbose("stopping idle server %s", server->name);
- server_stop(server);
- }
-}
-
-static void
-server_idle_connected_cb(struct connection *conn, bool connected)
-{
- struct server *server = container_of(conn, struct server, idle_conn);
-
- assert_return(conn);
- assert_task_alive(DBG_IDLE, &server->idle_task);
-
- if (!connected) {
- debug(DBG_IDLE,
- "idle check connection to remote server (%s) failed",
- server->name);
- server->idle_count = 0;
- server->state = SERVER_STATE_STOPPED;
- return;
- }
-
- debug(DBG_IDLE, "connected to remote %s\n", conn->remote.addrstr);
- idle_check_get_player_count(server, conn);
-}
-
-bool
-server_idle_check(struct server *server)
-{
- assert_return(server, false);
-
- if (server->state == SERVER_STATE_INIT ||
- server->state == SERVER_STATE_DEAD)
- return false;
-
- if (server->idle_timeout < 1)
- return false;
-
- if (list_empty(&server->remotes))
- return false;
-
- if (!list_empty(&server->proxys)) {
- server->idle_count = 0;
- return true;
- }
-
- connect_any(&server->idle_task, &server->remotes,
- &server->idle_conn, server_idle_connected_cb);
- return true;
-}
-
-static void
-server_announce_free(struct uring_task *task)
-{
- assert_return(task);
-
- debug(DBG_ANN, "called");
-}
-
-static void
-server_announce_cb(struct uring_task *task, int res)
-{
- struct server *server = container_of(task, struct server, ann_task);
-
- assert_return(task);
-
- if (res < 0)
- error("%s: failure %i", server->name, res);
- else if (res == server->ann_buf.len)
- debug(DBG_ANN, "%s: ok (%i)", server->name, res);
- else
- debug(DBG_ANN, "%s: unexpected result: %i", server->name, res);
-
- uring_task_set_fd(&server->ann_task, -1);
-}
-
-bool
-server_announce(struct server *server, int fd)
-{
- assert_return(server && fd >= 0, false);
-
- if (server->state == SERVER_STATE_INIT ||
- server->state == SERVER_STATE_DEAD)
- return false;
-
- debug(DBG_ANN, "announcing server: %s", server->name);
- uring_task_set_fd(&server->ann_task, fd);
- uring_tbuf_sendmsg(&server->ann_task, server_announce_cb);
- return true;
-}
-
-bool
-server_commit(struct server *server)
-{
- struct server_local *local;
- uint16_t port;
- int r;
-
- assert_return(server && server->name, false);
- assert_task_alive_or(DBG_SRV, &server->task, return false);
-
- if (server->state != SERVER_STATE_INIT) {
- error("called in wrong state");
- return false;
- }
-
- if (!list_empty(&server->proxys)) {
- error("%s: proxys not empty?", server->name);
- return false;
- }
-
- if (!list_empty(&server->dnslookups)) {
- debug(DBG_SRV, "called with pending DNS requests");
- return true;
- }
-
- if (server->stop_method == SERVER_STOP_METHOD_RCON &&
- list_empty(&server->rcons)) {
- error("%s: rcon stop method missing rcon address",
- server->name);
- return false;
- }
-
- if (server->stop_method == SERVER_STOP_METHOD_RCON &&
- !server->rcon_password) {
- error("%s: rcon stop method missing rcon password",
- server->name);
- return false;
- }
-
- if ((server->start_method == SERVER_START_METHOD_SYSTEMD ||
- server->stop_method == SERVER_STOP_METHOD_SYSTEMD) &&
- !server->systemd_service) {
- error("%s: systemd start/stop method missing systemd service",
- server->name);
- return false;
- }
-
- if (server->systemd_service && !server->systemd_obj) {
- server->systemd_obj = systemd_object_path(server->systemd_service);
- if (!server->systemd_obj) {
- error("%s: failed to create systemd object path (%s)",
- server->name, server->systemd_service);
- return false;
- }
- }
-
- if (server->idle_timeout > 0 &&
- server->stop_method == SERVER_STOP_METHOD_UNDEFINED) {
- error("%s: idle_timeout set but missing stop method", server->name);
- return false;
- }
-
- switch (server->type) {
- case SERVER_TYPE_ANNOUNCE:
- if (server->announce_port < 1) {
- error("%s: missing announce port", server->name);
- return false;
- }
-
- if (server->start_method != SERVER_START_METHOD_UNDEFINED) {
- error("%s: can't set start_method for announce server", server->name);
- return false;
- }
-
- if (!list_empty(&server->locals)) {
- error("%s: can't set local addresses for announce server", server->name);
- return false;
- }
-
- if (!list_empty(&server->remotes)) {
- error("%s: can't set remote addresses for announce server", server->name);
- return false;
- }
-
- break;
-
- case SERVER_TYPE_PROXY:
- if (server->announce_port >= 1) {
- error("%s: can't set announce port for proxy server", server->name);
- return false;
- }
-
- if (list_empty(&server->locals)) {
- error("%s: missing local addresses for proxy server", server->name);
- return false;
- }
-
- if (list_empty(&server->remotes)) {
- error("%s: missing remote addresses for proxy server", server->name);
- return false;
- }
-
- list_for_each_entry(local, &server->locals, list) {
- port = saddr_port(&local->local);
-
- if (port == 0) {
- error("%s: invalid local port", server->name);
- return false;
- }
-
- if (server->announce_port < 1)
- server->announce_port = port;
-
- if (server->announce_port != port) {
- error("%s: multiple local ports", server->name);
- return false;
- }
- }
-
- if (server->announce_port < 1) {
- error("%s: can't determine which port to announce", server->name);
- return false;
- }
-
- break;
-
- default:
- error("%s: can't determine server type", server->name);
- return false;
- }
-
- if (!server->pretty_name) {
- char *suffix;
-
- suffix = strrchr(server->name, '.');
- if (!suffix || suffix == server->name) {
- error("invalid server name: %s", server->name);
- return false;
- }
-
- server->pretty_name = xstrndup(server->name, suffix - server->name);
- if (!server->pretty_name) {
- error("failed to create display name: %s", server->name);
- return false;
- }
- }
-
- r = snprintf(server->ann_buf.buf, sizeof(server->ann_buf.buf),
- "[MOTD]%s[/MOTD][AD]%" PRIu16 "[/AD]",
- server->pretty_name, server->announce_port);
- if (r < 1 || r >= sizeof(server->ann_buf.buf)) {
- error("%s: unable to create announce msg: %i\n", server->name, r);
- return false;
- }
- server->ann_buf.len = r;
-
- /* FIXME: config, dont reread config if server running, make sure fd is available before this is called */
- server_dump(server);
-
- list_for_each_entry(local, &server->locals, list)
- local_open(local);
-
- server->state = SERVER_STATE_CFG_OK;
-
- server_check_running(server);
-
- debug(DBG_SRV, "success");
- return true;
-}
-
-bool
-server_add_remote(struct server *server, struct saddr *remote)
-{
- assert_return(server && remote, false);
- assert_task_alive_or(DBG_SRV, &server->task, return false);
-
- debug(DBG_SRV, "adding remote: %s", remote->addrstr);
- list_add(&remote->list, &server->remotes);
- return true;
-}
-
-bool
-server_add_local(struct server *server, struct saddr *saddr)
-{
- struct server_local *local;
-
- assert_return(server && saddr, false);
- assert_task_alive_or(DBG_SRV, &server->task, return false);
-
- local = local_new(server, saddr);
- if (!local)
- return false;
-
- list_add(&local->list, &server->locals);
- return true;
-}
-
-bool
-server_add_rcon(struct server *server, struct saddr *rcon)
-{
- assert_return(server && rcon, false);
- assert_task_alive_or(DBG_SRV, &server->task, return false);
-
- debug(DBG_SRV, "adding rcon: %s", rcon->addrstr);
- list_add(&rcon->list, &server->rcons);
- return true;
-}
-
-bool
-server_set_rcon_password(struct server *server, const char *password)
-{
- assert_return(server && !empty_str(password), false);
-
- return set_property(server, &server->rcon_password, password);
-}
-
-bool
-server_set_systemd_service(struct server *server, const char *service)
-{
- const char *suffix;
- char *tmp;
-
- assert_return(server && !empty_str(service) && !server->systemd_service, false);
-
- suffix = strrchr(service, '.');
- if (!suffix || !streq(suffix, ".service")) {
- tmp = zmalloc(strlen(service) + strlen(".service") + 1);
- if (tmp)
- sprintf(tmp, "%s.service", service);
- } else
- tmp = xstrdup(service);
-
- if (!tmp) {
- error("malloc/strdup: %m");
- return false;
- }
-
- server->systemd_service = tmp;
- return true;
-}
-
-bool
-server_set_stop_method(struct server *server,
- enum server_stop_method stop_method)
-{
- assert_return(server->stop_method == SERVER_STOP_METHOD_UNDEFINED &&
- stop_method != SERVER_STOP_METHOD_UNDEFINED, false);
-
- server->stop_method = stop_method;
- return true;
-}
-
-bool
-server_set_start_method(struct server *server,
- enum server_start_method start_method)
-{
- assert_return(server->start_method == SERVER_START_METHOD_UNDEFINED &&
- start_method != SERVER_START_METHOD_UNDEFINED, false);
-
- server->start_method = start_method;
- return true;
-}
-
-bool
-server_set_stop_exec(struct server *server, const char *cmd)
-{
- assert_return(server && !empty_str(cmd), false);
-
- return set_property(server, &server->stop_exec, cmd);
-}
-
-bool
-server_set_start_exec(struct server *server, const char *cmd)
-{
- assert_return(server && !empty_str(cmd), false);
-
- return set_property(server, &server->start_exec, cmd);
-}
-
-bool
-server_set_idle_timeout(struct server *server, uint16_t timeout)
-{
- assert_return(server && timeout > 0 && server->idle_timeout == 0, false);
-
- server->idle_timeout = timeout;
- return true;
-}
-
-bool
-server_set_port(struct server *server, uint16_t port)
-{
- assert_return(server && port > 0 && server->announce_port == 0, false);
-
- server->announce_port = htons(port);
- return true;
-}
-
-bool
-server_set_type(struct server *server, enum server_type type)
-{
- assert_return(server && type != SERVER_TYPE_UNDEFINED, false);
-
- switch (type) {
- case SERVER_TYPE_ANNOUNCE:
- server->type = SERVER_TYPE_ANNOUNCE;
- break;
- case SERVER_TYPE_PROXY:
- server->type = SERVER_TYPE_PROXY;
- break;
- default:
- return false;
- }
-
- return true;
-}
-
-bool
-server_set_pretty_name(struct server *server, const char *pretty_name)
-{
- assert_return(server && !empty_str(pretty_name), false);
-
- return set_property(server, &server->pretty_name, pretty_name);
-}
-
-struct server *
-server_new(const char *name)
-{
- struct server *server;
-
- assert_return(!empty_str(name), NULL);
-
- list_for_each_entry(server, &cfg->servers, list) {
- if (!streq(name, server->name))
- continue;
- error("attempt to add duplicate server: %s", name);
- return server;
- }
-
- verbose("Adding server %s", name);
- server = zmalloc(sizeof(*server));
- if (!server) {
- error("malloc: %m");
- return NULL;
- }
-
- server->state = SERVER_STATE_INIT;
- server->type = SERVER_TYPE_UNDEFINED;
- server->name = xstrdup(name);
- server->stop_method = SERVER_STOP_METHOD_UNDEFINED;
- server->start_method = SERVER_START_METHOD_UNDEFINED;
- server->idle_timeout = 0;
-
- uring_task_init(&server->task, server->name, uring_parent(), server_free);
- uring_task_set_buf(&server->task, &server->tbuf);
-
- uring_task_init(&server->ann_task, "announce", &server->task, server_announce_free);
- uring_task_set_buf(&server->ann_task, &server->ann_buf);
- saddr_set_ipv4(&server->ann_task.saddr, cinet_addr(224,0,2,60), htons(4445));
-
- uring_task_init(&server->exec_task, "exec", &server->task, server_exec_free);
-
- uring_task_init(&server->idle_task, "idle", &server->task, server_idle_free);
- uring_task_set_buf(&server->idle_task, &server->idle_buf);
-
- rcon_init(server);
-
- list_init(&server->remotes);
- list_init(&server->locals);
- list_init(&server->proxys);
- list_init(&server->rcons);
- list_init(&server->dnslookups);
- list_add(&server->list, &cfg->servers);
-
- return server;
-}