summaryrefslogtreecommitdiff
path: root/minecctl/minecctl.c
diff options
context:
space:
mode:
authorDavid Härdeman <david@hardeman.nu>2020-06-26 03:23:41 +0200
committerDavid Härdeman <david@hardeman.nu>2020-06-26 03:23:41 +0200
commit06a2edfc72a894054ed710d338504242f15d6071 (patch)
tree00ab94400cab0a5d46dbd68648539003732eb19e /minecctl/minecctl.c
parentc9575bb333c036e5470d0ff872169cdb4626e023 (diff)
Make the server handling in minecctl saner
Diffstat (limited to 'minecctl/minecctl.c')
-rw-r--r--minecctl/minecctl.c468
1 files changed, 167 insertions, 301 deletions
diff --git a/minecctl/minecctl.c b/minecctl/minecctl.c
index 7c01736..c8cc34d 100644
--- a/minecctl/minecctl.c
+++ b/minecctl/minecctl.c
@@ -2,7 +2,6 @@
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
-#include <stdarg.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -19,143 +18,68 @@
#include "minecctl-rcon.h"
#include "config-parser.h"
#include "server-config-options.h"
+#include "misc.h"
#include "config.h"
static struct cfg *cfg = NULL;
-bool use_colors = false;
-
-/* FIXME: Can be shared */
static void
-set_use_colors()
-{
- int fd;
- const char *e;
-
- if (getenv("NO_COLOR"))
- return;
-
- fd = fileno(stderr);
- if (fd < 0)
- return;
-
- if (!isatty(fd))
- return;
-
- e = getenv("TERM");
- if (!e)
- return;
-
- if (streq(e, "dumb"))
- return;
-
- use_colors = true;
-}
-
-void
-__debug(_unused_ enum debug_lvl lvl, const char *fmt, ...)
-{
- va_list ap;
-
- if (use_colors) {
- if (lvl & DBG_ERROR)
- fprintf(stderr, ANSI_RED);
- else if (!(lvl & (DBG_INFO | DBG_VERBOSE)))
- fprintf(stderr, ANSI_GREY);
- }
-
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
-
- if (use_colors && !(lvl & (DBG_INFO | DBG_VERBOSE)))
- fprintf(stderr, ANSI_NORMAL);
-}
-
-_noreturn_ void
-__die(const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
-
- exit(EXIT_FAILURE);
-}
-
-void *
-__zmalloc(const char *fn, int line, size_t size)
+dump_config()
{
- void *ptr;
-
- assert_die(!empty_str(fn) && line > 0 && size > 0, "invalid arguments");
-
- ptr = calloc(1, size);
- if (!ptr)
- die("malloc: %m");
- return ptr;
+ info("Configuration:");
+ info("password : %s", cfg->password);
+ info("cfgdir : %s", cfg->cfgdir);
+ info("addrstr : %s", cfg->addrstr);
+ info("cmdstr : %s", cfg->cmdstr);
+ info("cmd : %p", cfg->cmd);
+ info("force stop : %s", cfg->force_stop ? "yes" : "no");
+ info("servers : %sempty", list_empty(&cfg->servers) ? "" : "not ");
+ /* FIXME: dump servers */
}
-char *
-__xstrdup(const char *fn, int line, const char *s)
+void
+read_server_config(struct server *server)
{
- char *ptr;
-
- assert_die(!empty_str(fn) && line > 0 && !empty_str(s), "invalid arguments");
-
- ptr = strdup(s);
- if (!ptr)
- die("strdup: %m");
- return ptr;
-}
+ char buf[4096];
+ size_t off = 0;
+ ssize_t r;
+ int dfd;
+ int fd;
+ char *pos = buf;
-char *
-__xstrndup(const char *fn, int line, const char *s, size_t n)
-{
- char *ptr;
+ if (!server || !server->filename || server->file_read)
+ return;
- assert_die(!empty_str(fn) && line > 0 && !empty_str(s) && n > 0, "invalid arguments");
+ server->file_read = true;
- ptr = strndup(s, n);
- if (ptr)
- die("strdup: %m");
- return ptr;
-}
+ dfd = open(cfg->cfgdir, O_DIRECTORY | O_PATH | O_CLOEXEC);
+ if (dfd < 0)
+ die("Failed to open %s: %m", cfg->cfgdir);
-void
-__xfree(const char *fn, int line, void *ptr)
-{
- assert_die(!empty_str(fn) && line > 0, "invalid arguments");
+ fd = openat(dfd, server->filename, O_RDONLY | O_CLOEXEC);
+ if (fd < 0)
+ die("Failed to open %s: %m", server->filename);
- free(ptr);
-}
+ close(dfd);
-static void
-dump_config()
-{
- assert_die(cfg, "cfg not set");
+ while (true) {
+ r = read(fd, buf + off, sizeof(buf) - off - 1);
+ if (r < 0)
+ die("Failed to read %s: %m", server->filename);
+ else if (r == 0)
+ break;
- info("Configuration:");
- info("password : %s", cfg->password);
- info("cfgdir : %s", cfg->cfgdir);
- info("addrstr : %s", cfg->addrstr);
- info("cmdstr : %s", cfg->cmdstr);
- info("server : %p", cfg->server);
- info("cmd : %p", cfg->cmd);
- info("force stop : %s", cfg->force_stop ? "yes" : "no");
- info("addrs : %sempty", list_empty(&cfg->addrs) ? "" : "not ");
- info("mcaddrs : %sempty", list_empty(&cfg->mcaddrs) ? "" : "not ");
- info("known_servers : %sempty", list_empty(&cfg->addrs) ? "" : "not ");
-}
+ off += r;
+ if (off == sizeof(buf) - 1)
+ die("Failed to read %s: file too large", server->filename);
+ }
-static void
-parse_server_config(char *buf, const char *filename)
-{
- assert_die(buf && filename && cfg, "invalid arguments");
+ buf[off] = '\0';
+ close(fd);
- if (!config_parse_header(SERVER_CFG_HEADER, &buf))
- die("Unable to parse %s: invalid/missing header", filename);
+ if (!config_parse_header(SERVER_CFG_HEADER, &pos))
+ die("Unable to parse %s: invalid/missing header",
+ server->filename);
/* FIXME: this will cause superflous DNS lookups of other cfg entries */
while (true) {
@@ -163,82 +87,41 @@ parse_server_config(char *buf, const char *filename)
const char *keyname;
struct cfg_value value;
- if (!config_parse_line(filename, &buf, scfg_key_map,
+ if (!config_parse_line(server->filename, &pos, scfg_key_map,
&key, &keyname, &value, false))
break;
switch (key) {
case SCFG_KEY_RCON:
- if (!list_empty(&cfg->addrs))
- die("rcon address defined twice in %s", filename);
- list_replace(&value.saddrs, &cfg->addrs);
+ if (!list_empty(&server->rcon_addrs))
+ die("rcon address defined twice in %s",
+ server->filename);
+ list_replace(&value.saddrs, &server->rcon_addrs);
break;
case SCFG_KEY_RCON_PASSWORD:
- if (!cfg->password)
- cfg->password = xstrdup(value.str);
+ if (server->rcon_password)
+ die("rcon password defined twice in %s",
+ server->filename);
+ server->rcon_password = xstrdup(value.str);
break;
case SCFG_KEY_REMOTE:
- if (!list_empty(&cfg->mcaddrs))
- die("rcon address defined twice in %s", filename);
- list_replace(&value.saddrs, &cfg->mcaddrs);
+ if (!list_empty(&server->mc_addrs))
+ die("rcon address defined twice in %s",
+ server->filename);
+ list_replace(&value.saddrs, &server->mc_addrs);
default:
continue;
}
-
- if (cfg->password &&
- !list_empty(&cfg->addrs) &&
- !list_empty(&cfg->mcaddrs))
- break;
}
- if (!cfg->password)
- verbose("rcon password not found in %s", filename);
+ if (!server->rcon_password)
+ verbose("rcon password not found in %s", server->filename);
- if (list_empty(&cfg->addrs))
- verbose("rcon address not found in %s", filename);
+ if (list_empty(&server->rcon_addrs))
+ verbose("rcon address not found in %s", server->filename);
- if (list_empty(&cfg->mcaddrs))
- verbose("mc server address not found in %s", filename);
-}
-
-static void
-read_server_config()
-{
- char buf[4096];
- size_t off = 0;
- ssize_t r;
- int dfd;
- int fd;
-
- assert_die(cfg && cfg->server && cfg->cfgdir, "invalid arguments");
-
- dfd = open(cfg->cfgdir, O_DIRECTORY | O_PATH | O_CLOEXEC);
- if (dfd < 0)
- die("Failed to open %s: %m", cfg->cfgdir);
-
- fd = openat(dfd, cfg->server->filename, O_RDONLY | O_CLOEXEC);
- if (fd < 0)
- die("Failed to open %s: %m", cfg->server->filename);
-
- close(dfd);
-
- while (true) {
- r = read(fd, buf + off, sizeof(buf) - off - 1);
- if (r < 0)
- die("Failed to read %s: %m", cfg->server->filename);
- else if (r == 0)
- break;
-
- off += r;
- if (off == sizeof(buf) - 1)
- die("Failed to read %s: file too large", cfg->server->filename);
- }
-
- buf[off] = '\0';
- close(fd);
-
- parse_server_config(buf, cfg->server->filename);
- dump_config();
+ if (list_empty(&server->mc_addrs))
+ verbose("mc server address not found in %s", server->filename);
}
_noreturn_ static void
@@ -279,32 +162,21 @@ usage(bool no_error)
exit(no_error ? EXIT_FAILURE : EXIT_SUCCESS);
}
-static char *
-strv_join(char * const *strv)
+static struct server *
+allocate_server()
{
- size_t len = 0;
- char *r, *to;
-
- for (unsigned i = 0; strv[i]; i++)
- len += strlen(strv[i]) + 1;
-
- if (len == 0)
- return NULL;
-
- r = zmalloc(len);
- to = r;
+ struct server *server;
- for (unsigned i = 0; strv[i]; i++) {
- if (i > 0)
- *(to++) = ' ';
- to = stpcpy(to, strv[i]);
- }
+ server = zmalloc(sizeof(*server));
+ list_init(&server->rcon_addrs);
+ list_init(&server->mc_addrs);
+ list_init(&server->list);
- return r;
+ return server;
}
static void
-get_known_servers()
+get_servers()
{
struct dirent *dent;
DIR *dir;
@@ -322,7 +194,7 @@ get_known_servers()
if (!is_valid_server_config_filename(dent, NULL))
continue;
- server = zmalloc(sizeof(*server));
+ server = allocate_server();
server->filename = xstrdup(dent->d_name);
suffix = strrchr(dent->d_name, '.');
@@ -330,112 +202,116 @@ get_known_servers()
*suffix = '\0';
server->shortname = xstrdup(dent->d_name);
- list_add(&server->list, &cfg->known_servers);
+ list_add(&server->list, &cfg->servers);
}
closedir(dir);
}
-static void
-do_list(struct cfg *cfg)
-{
- struct server *server;
-
- list_for_each_entry(server, &cfg->known_servers, list)
- info("%s", server->shortname);
-}
-
-static void
-set_server(const char *name)
+static bool
+str_to_addrs(const char *str, struct list_head *list)
{
- struct server *server;
+ struct cfg_value value;
+ char *tmp = NULL;
+ bool rv = false;
+
+ /* strtosockaddrs mangles the input string */
+ tmp = xstrdup(str);
+ if (!strtosockaddrs(tmp, &value, false)) {
+ error("Unable to parse address: %s", str);
+ goto out;
+ }
- assert_die(cfg, "invalid arguments");
- assert_die(!cfg->server, "can't set server twice");
+ if (value.type != CFG_VAL_TYPE_ADDRS) {
+ error("Unexpected return value from strtosockaddrs");
+ goto out;
+ }
- list_for_each_entry(server, &cfg->known_servers, list) {
- if (streq(name, server->shortname)) {
- cfg->server = server;
- return;
- }
+ if (list_empty(&value.saddrs)) {
+ error("Found no valid addresses for %s", str);
+ goto out;
}
- error("\"%s\" is not a known server or command", name);
- usage(false);
+ list_replace(&value.saddrs, list);
+ rv = true;
+
+out:
+ xfree(tmp);
+ return rv;
}
-char *
-ask_password()
+static bool
+create_server_from_cmdline_args()
{
- struct termios old, new;
- char *password = NULL;
- size_t len = 0;
- ssize_t r;
+ struct server *server;
- assert_die(cfg, "invalid arguments");
+ if (!cfg->addrstr && !cfg->mcaddrstr)
+ return false;
- if (!isatty(STDIN_FILENO))
- return NULL;
+ server = allocate_server();
- if (tcgetattr(STDIN_FILENO, &old) < 0)
- return NULL;
+ if (cfg->addrstr) {
+ if (!str_to_addrs(cfg->addrstr, &server->rcon_addrs))
+ goto error;
- new = old;
- new.c_lflag &= ~ECHO;
- new.c_lflag |= ICANON;
- if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &new) < 0)
- return NULL;
+ server->shortname = cfg->addrstr;
+ cfg->addrstr = NULL;
+ }
- fprintf(stderr, "Password: ");
- r = getline(&password, &len, stdin);
+ if (cfg->mcaddrstr) {
+ if (!str_to_addrs(cfg->mcaddrstr, &server->mc_addrs))
+ goto error;
- tcsetattr(STDIN_FILENO, TCSAFLUSH, &old);
+ if (!server->shortname)
+ server->shortname = cfg->mcaddrstr;
+ else
+ xfree(cfg->mcaddrstr);
- if (r < 0) {
- info("Error in getline: %m");
- clearerr(stdin);
- free(password);
- return NULL;
+ cfg->mcaddrstr = NULL;
}
- while (r > 0 && password[r - 1] == '\n')
- password[--r] = '\0';
+ if (cfg->password) {
+ server->rcon_password = cfg->password;
+ cfg->password = NULL;
+ }
- return password;
+ list_add(&server->list, &cfg->servers);
+ return true;
+
+error:
+ /* FIXME: free addrs */
+ xfree(server->shortname);
+ xfree(server);
+ return false;
}
-int
-connect_any(struct list_head *addrs, bool may_fail)
+static void
+do_list(struct cfg *cfg)
{
- struct saddr *saddr;
- bool connected = false;
- int sfd;
+ struct server *server;
- if (list_empty(addrs))
- die("No address to connect to");
+ list_for_each_entry(server, &cfg->servers, list)
+ info("%s", server->shortname);
+}
- list_for_each_entry(saddr, addrs, list) {
- sfd = socket(saddr->storage.ss_family, SOCK_STREAM | SOCK_CLOEXEC, 0);
- if (sfd < 0)
- die("socket: %m");
+static void
+set_server(const char *name)
+{
+ struct server *server;
- socket_set_low_latency(sfd, true, true, true);
+ assert_die(cfg, "invalid arguments");
- if (connect(sfd, (struct sockaddr *)&saddr->storage, saddr->addrlen) < 0) {
- close(sfd);
- continue;
+ list_for_each_entry(server, &cfg->servers, list) {
+ if (streq(name, server->shortname)) {
+ /* This puts the chosen server first in the list */
+ list_del(&server->list);
+ list_add(&server->list, &cfg->servers);
+ return;
}
-
- connected = true;
- break;
}
- if (!connected && may_fail)
- return -1;
- else if (!connected)
- die("Failed to connect to remote host");
- else
- return sfd;
+ error("\"%s\" is not a known server or command", name);
+ usage(false);
}
static inline void
@@ -561,7 +437,6 @@ parse_cmdline(int argc, char * const *argv)
usage(false);
}
- list_init(&cfg->addrs);
cfg->cfgdir = DEFAULT_CFG_DIR;
while (true) {
@@ -632,52 +507,43 @@ parse_cmdline(int argc, char * const *argv)
int
main(int argc, char **argv)
{
+ int rv = EXIT_FAILURE;
+
debug_mask = DBG_ERROR | DBG_INFO;
set_use_colors();
cfg = zmalloc(sizeof(*cfg));
- list_init(&cfg->addrs);
- list_init(&cfg->mcaddrs);
- list_init(&cfg->known_servers);
+ list_init(&cfg->servers);
parse_cmdline(argc, argv);
- get_known_servers();
+ get_servers();
parse_command(&argv[optind]);
- if (!cfg->cmd)
- die("Failed to parse command");
-
- if (cfg->server) {
- read_server_config();
-
- } else if (cfg->addrstr) {
- struct cfg_value value;
-
- /* FIXME: create a struct server here, fill in details like name */
- if (!strtosockaddrs(cfg->addrstr, &value, false))
- die("Unable to parse address: %s", cfg->addrstr);
-
- if (value.type != CFG_VAL_TYPE_ADDRS)
- die("Unexpected return value from strtosockaddrs");
-
- if (list_empty(&value.saddrs))
- die("Found no valid addresses for %s", cfg->addrstr);
-
- list_replace(&value.saddrs, &cfg->addrs);
+ if (!cfg->cmd) {
+ error("Failed to parse command");
+ goto out;
}
+ if (cfg->addrstr || cfg->mcaddrstr)
+ if (!create_server_from_cmdline_args())
+ goto out;
+
+ /* FIXME: Should return bool */
cfg->cmd(cfg);
+ rv = EXIT_SUCCESS;
+
+out:
+ /* FIXME: Not enough cleanup */
if (cfg->password)
explicit_bzero(cfg->password, strlen(cfg->password));
-
xfree(cfg->password);
xfree(cfg->addrstr);
xfree(cfg->mcaddrstr);
xfree(cfg);
- exit(EXIT_SUCCESS);
+ exit(rv);
}