summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Härdeman <david@hardeman.nu>2020-07-09 20:36:15 +0200
committerDavid Härdeman <david@hardeman.nu>2020-07-09 20:36:15 +0200
commitdd6321c0acf7b0570811200a205cc4104bee49c7 (patch)
tree52da75427a0c1d237806fbebba23025afd79d28d
parent4ae60696aed938347cc1cf2a5d8f5a2b86292132 (diff)
Implement a basic init command in minecctl to create an initial example config
-rw-r--r--minecctl/minecctl-commands.h5
-rw-r--r--minecctl/minecctl.c25
-rw-r--r--minecctl/minecctl.h7
-rw-r--r--minecctl/misc-commands.c241
-rw-r--r--minecctl/misc-commands.h2
-rw-r--r--minecctl/misc.c73
-rw-r--r--minecctl/misc.h14
-rw-r--r--minecctl/rcon-commands.c33
-rw-r--r--minecctl/server.c117
-rw-r--r--minecctl/server.h4
10 files changed, 448 insertions, 73 deletions
diff --git a/minecctl/minecctl-commands.h b/minecctl/minecctl-commands.h
index 909c48d..16cf511 100644
--- a/minecctl/minecctl-commands.h
+++ b/minecctl/minecctl-commands.h
@@ -4,6 +4,7 @@
enum commands {
CMD_INVALID = 0,
+ CMD_INIT,
CMD_LIST,
CMD_LINT,
CMD_INFO,
@@ -20,6 +21,10 @@ static struct command_list {
const char *name;
enum commands cmd;
} command_list[] = { {
+ .name = "init",
+ .cmd = CMD_INIT,
+ },
+ {
.name = "list",
.cmd = CMD_LIST,
},
diff --git a/minecctl/minecctl.c b/minecctl/minecctl.c
index 56e386e..b374f18 100644
--- a/minecctl/minecctl.c
+++ b/minecctl/minecctl.c
@@ -20,7 +20,6 @@
#include "rcon-commands.h"
#include "misc-commands.h"
#include "misc.h"
-#include "config.h"
static void dump_server(struct server *server)
{
@@ -131,12 +130,15 @@ void dump_config(struct cfg *cfg)
info("Configuration");
info("┌────────────");
- info("│ cfgdir : %s", cfg->cfgdir);
+ info("│ dir_path : %s", cfg->dir_path);
+ info("│ dir : %p", cfg->dir);
info("│ rcon_password : %s", cfg->rcon_password);
info("│ rcon_addrstr : %s", cfg->rcon_addrstr);
info("│ mc_addrstr : %s", cfg->mc_addrstr);
info("│ cmd : %p", cfg->cmd);
info("│ force stop : %s", cfg->force_stop ? "yes" : "no");
+ info("│ default set : %s", cfg->default_set ? "yes" : "no");
+ info("│ servers loaded: %s", cfg->server_list_loaded ? "yes" : "no");
info("│ commands%s", cfg->commands ? "" : " : none");
if (cfg->commands) {
for (char *const *cmd = cfg->commands; *cmd; cmd++)
@@ -154,6 +156,7 @@ _noreturn_ static void usage(bool no_error)
info("Usage: %s [OPTIONS...] COMMAND\n"
"\n"
"Valid commands:\n"
+ " init perform initial setup\n"
" list list known servers\n"
" lint check validity of server configuration files\n"
" info [SERVER] show information about a running SERVER (or all known servers)\n"
@@ -176,7 +179,6 @@ _noreturn_ static void usage(bool no_error)
" (only relevant for some commands, can also\n"
" use environment variable MC_ADDRESS)\n"
" -c, --cfgdir=DIR look for server configuration files in DIR\n"
- " (default: %s)\n"
" -f, --force stop server even if it has players\n"
" -v, --verbose enable extra logging\n"
" -d, --debug enable debugging information\n"
@@ -186,7 +188,7 @@ _noreturn_ static void usage(bool no_error)
" the command and vice versa.\n"
"\n"
"See the minecctl(1) man page for details.\n",
- program_invocation_short_name, DEFAULT_CFG_DIR);
+ program_invocation_short_name);
exit(no_error ? EXIT_FAILURE : EXIT_SUCCESS);
}
@@ -315,6 +317,13 @@ static void parse_command(struct cfg *cfg, char *const *argv)
}
switch (cmd) {
+ case CMD_INIT:
+ if (*argv) {
+ error("Too many arguments");
+ usage(false);
+ }
+ cfg->cmd = do_init;
+ break;
case CMD_LIST:
if (*argv) {
error("Too many arguments");
@@ -402,8 +411,6 @@ static void parse_cmdline(struct cfg *cfg, int argc, char *const *argv)
usage(false);
}
- cfg->cfgdir = DEFAULT_CFG_DIR;
-
while (true) {
int option_index = 0;
/* clang-format off */
@@ -437,7 +444,7 @@ static void parse_cmdline(struct cfg *cfg, int argc, char *const *argv)
cfg->mc_addrstr = xstrdup(optarg);
break;
case 'c':
- cfg->cfgdir = optarg;
+ cfg->dir_path = optarg;
break;
case 'v':
debug_mask |= DBG_VERBOSE;
@@ -488,8 +495,6 @@ int main(int argc, char *const *argv)
parse_cmdline(&cfg, argc, argv);
- server_load_all_known(&cfg);
-
parse_command(&cfg, &argv[optind]);
if (!cfg.cmd) {
@@ -509,5 +514,7 @@ out:
strv_free(cfg.commands);
xfree(cfg.rcon_addrstr);
xfree(cfg.mc_addrstr);
+ if (cfg.dir)
+ closedir(cfg.dir);
exit(success ? EXIT_SUCCESS : EXIT_FAILURE);
}
diff --git a/minecctl/minecctl.h b/minecctl/minecctl.h
index 16199a3..46c616a 100644
--- a/minecctl/minecctl.h
+++ b/minecctl/minecctl.h
@@ -2,14 +2,19 @@
#ifndef foominecctlhfoo
#define foominecctlhfoo
+#include <sys/types.h>
+#include <dirent.h>
+
struct cfg {
- const char *cfgdir;
+ const char *dir_path;
+ DIR *dir;
char *rcon_password;
char *rcon_addrstr;
char *mc_addrstr;
char **commands;
bool force_stop;
bool default_set;
+ bool server_list_loaded;
bool (*cmd)(struct cfg *cfg);
struct list_head servers;
};
diff --git a/minecctl/misc-commands.c b/minecctl/misc-commands.c
index e23de37..054279b 100644
--- a/minecctl/misc-commands.c
+++ b/minecctl/misc-commands.c
@@ -1,4 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0 */
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+
#include "shared/utils.h"
#include "minecctl.h"
#include "server.h"
@@ -6,11 +15,239 @@
#include "rcon-commands.h"
#include "mc-commands.h"
#include "shared/systemd.h"
+#include "misc.h"
+
+static bool create_link(int dfd, const char *source, const char *target)
+{
+ if (symlinkat(target, dfd, source) < 0) {
+ error("Unable to create link %s -> %s: %m", source, target);
+ return false;
+ }
+
+ return true;
+}
+
+static bool write_cfg_file(int dfd, const char *name, const char *content)
+{
+ int fd;
+ ssize_t len = strlen(content);
+ ssize_t done = 0, r;
+ bool rv = true;
+
+ fd = openat(dfd, name, O_WRONLY | O_CLOEXEC | O_CREAT | O_EXCL, 0644);
+ if (fd < 0) {
+ error("Unable to create file %s: %m", name);
+ return false;
+ }
+
+ while (done < len) {
+ r = write(fd, content + done, len - done);
+ if (r < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ error("Unable to write file %s: %m", name);
+ rv = false;
+ break;
+ }
+
+ done += r;
+ }
+
+ close(fd);
+ return rv;
+}
+
+static const char txt_server_readme[] = "This is the server readme\n";
+static const char txt_server_eula[] = "This is the server eula\n";
+static const char txt_server_properties[] = "This is the server properties\n";
+static const char txt_server_conf[] = "This is the server conf\n";
+static const char txt_minecproxy_conf[] = "This is the minecproxy conf\n";
+static const char txt_minecproxy_service[] = "This is the minecproxy service\n";
+static const char txt_minecserver_service[] = "This is the minecserver service\n";
+
+static bool find_user_service(int xfd, const char *service)
+{
+ int dfd;
+ /* FIXME: Make this a macro, make paths #defines */
+ char sub_path[STRLEN("systemd/user/") + strlen(service) + 1];
+ char etc_path[STRLEN("/etc/systemd/user/") + strlen(service) + 1];
+ char usr_path[STRLEN("/usr/lib/systemd/user/") + strlen(service) + 1];
+
+ sprintf(sub_path, "systemd/user/%s", service);
+ sprintf(etc_path, "/etc/systemd/user/%s", service);
+ sprintf(usr_path, "/usr/lib/systemd/user/%s", service);
+
+ /*
+ * We need to check (in order of precedence):
+ * ~/.config/systemd/user/ - user-created
+ * /etc/systemd/user/ - admin-created
+ * ~/.local/share/systemd/user/ - user-installed packages
+ * /usr/lib/systemd/user/ - system-installed packages
+ */
+
+ if (faccessat(xfd, sub_path, R_OK, 0) == 0) {
+ info("User service %s already installed in "
+ "$XDG_CONFIG_HOME/systemd/user/", service);
+ return true;
+ }
+
+ if (access(etc_path, R_OK) == 0) {
+ info("User service %s already installed in "
+ "/etc/systemd/user/", service);
+ return true;
+ }
+
+ dfd = open_xdg_shared_dir();
+ if (dfd >= 0) {
+ if (faccessat(dfd, sub_path, R_OK, 0) == 0) {
+ info("User service %s already installed in "
+ "$XDG_DATA_HOME/systemd/user/", service);
+ close(dfd);
+ return true;
+ }
+ close(dfd);
+ }
+
+ if (access(usr_path, R_OK) == 0) {
+ info("User service %s already installed in "
+ "/usr/lib/systemd/user/", service);
+ return true;
+ }
+
+ return false;
+}
+
+static bool create_user_service(int xfd, const char *service,
+ const char *content)
+{
+ int sfd, ufd = -1;
+ bool rv = false;
+
+ if (find_user_service(xfd, service))
+ return true;
+
+ sfd = open_subdir(xfd, "systemd", true);
+ if (sfd < 0)
+ goto out;
+
+ ufd = open_subdir(sfd, "user", true);
+ if (ufd < 0)
+ goto out;
+
+ if (!write_cfg_file(ufd, service, content))
+ goto out;
+
+ info("Created user service file $XDG_CONFIG_HOME/systemd/user/%s",
+ service);
+ rv = true;
+
+out:
+ if (ufd >= 0)
+ close(ufd);
+ if (sfd >= 0)
+ close(sfd);
+ return rv;
+}
+
+static bool create_config_tree(int xfd)
+{
+ int mfd = -1, sfd = -1, efd = -1, cfd = -1;
+ bool rv = false;
+
+ mfd = open_subdir(xfd, "minecproxy", true);
+ if (mfd < 0)
+ goto out;
+
+ sfd = open_subdir(mfd, "servers", true);
+ if (sfd < 0)
+ goto out;
+
+ if (!write_cfg_file(sfd, "README.TXT", txt_server_readme))
+ goto out;
+
+ efd = open_subdir(sfd, "example-server", true);
+ if (efd < 0)
+ goto out;
+
+ if (!write_cfg_file(efd, "eula.txt", txt_server_eula))
+ goto out;
+
+ if (!write_cfg_file(efd, "server.properties", txt_server_properties))
+ goto out;
+
+ if (!create_link(efd, "server.jar", "../server.jar"))
+ goto out;
+
+ cfd = open_subdir(mfd, "config", true);
+ if (cfd < 0)
+ goto out;
+
+ if (!write_cfg_file(cfd, "example-server.mcserver", txt_server_conf))
+ goto out;
+
+ if (!write_cfg_file(cfd, "minecproxy.conf", txt_minecproxy_conf))
+ goto out;
+
+ info("Created configuration in $XDG_CONFIG_HOME/minecproxy");
+ rv = true;
+
+out:
+ if (mfd >= 0)
+ close(mfd);
+ if (sfd >= 0)
+ close(sfd);
+ if (efd >= 0)
+ close(efd);
+ if (cfd >= 0)
+ close(cfd);
+ return rv;
+}
+
+bool do_init(struct cfg *cfg)
+{
+ int xfd;
+ bool rv = false;
+
+ if (cfg->dir_path) {
+ xfd = open(cfg->dir_path, O_PATH | O_CLOEXEC | O_DIRECTORY);
+ if (xfd < 0)
+ error("Failed to open dir %s: %m", cfg->dir_path);
+ } else
+ xfd = open_xdg_cfg_dir(true);
+
+ if (xfd < 0)
+ goto out;
+
+ if (!create_config_tree(xfd))
+ goto out;
+
+ if (cfg->dir_path) {
+ info("Option -c used, not creating systemd services");
+ rv = true;
+ goto out;
+ }
+
+ if (!create_user_service(xfd, "minecserver@.service",
+ txt_minecserver_service))
+ goto out;
+
+ if (!create_user_service(xfd, "minecproxy.service",
+ txt_minecproxy_service))
+ goto out;
+
+ rv = true;
+out:
+ if (xfd >= 0)
+ close(xfd);
+ return rv;
+}
bool do_list(struct cfg *cfg)
{
struct server *server;
+ server_load_all_known(cfg);
+
/* server->scfg.filename check excludes servers created from cmdline */
list_for_each_entry(server, &cfg->servers, list)
if (server->scfg.filename)
@@ -112,10 +349,8 @@ bool do_pcount(struct cfg *cfg)
const char *error;
server = server_get_default(cfg);
- if (!server) {
- error("failed to get default server");
+ if (!server)
return false;
- }
if (do_rcon_pcount(cfg, server, &online, &max, &error))
info("Rcon says %u/%u", online, max);
diff --git a/minecctl/misc-commands.h b/minecctl/misc-commands.h
index ebb6ccd..95d0ba0 100644
--- a/minecctl/misc-commands.h
+++ b/minecctl/misc-commands.h
@@ -2,6 +2,8 @@
#ifndef foomisccommandshfoo
#define foomisccommandshfoo
+bool do_init(struct cfg *cfg);
+
bool do_list(struct cfg *cfg);
bool do_lint(struct cfg *cfg);
diff --git a/minecctl/misc.c b/minecctl/misc.c
index 525b09a..26cbc86 100644
--- a/minecctl/misc.c
+++ b/minecctl/misc.c
@@ -5,11 +5,84 @@
#include <stdarg.h>
#include <string.h>
#include <termios.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <errno.h>
#include "shared/utils.h"
#include "misc.h"
#include "minecctl.h"
+static const char *get_homedir()
+{
+ const char *e;
+ struct passwd *passwd;
+ uid_t uid;
+
+ e = getenv("HOME");
+ if (e && e[0] == '/')
+ return e;
+
+ uid = getuid();
+ if (uid == 0)
+ return "/root";
+
+ passwd = getpwuid(uid);
+ if (passwd && passwd->pw_dir[0] == '/')
+ return passwd->pw_dir;
+
+ return NULL;
+}
+
+int open_subdir(int dfd, const char *subdir, bool nofail)
+{
+ int sfd;
+
+ if (nofail && mkdirat(dfd, subdir, 0777) < 0 && errno != EEXIST) {
+ error("Unable to create subdirectory %s: %m", subdir);
+ return -1;
+ }
+
+ sfd = openat(dfd, subdir, O_PATH | O_CLOEXEC | O_DIRECTORY);
+ if (sfd < 0 && nofail)
+ error("Unable to open subdirectory %s: %m", subdir);
+
+ return sfd;
+}
+
+int open_xdg_dir(const char *envname, const char *altpath, bool nofail)
+{
+ const char *e;
+ const char *h;
+ int dfd, hfd;
+
+ e = getenv(envname);
+ if (e && e[0] == '/') {
+ dfd = open(e, O_PATH | O_CLOEXEC | O_DIRECTORY);
+ if (dfd < 0)
+ error("Unable to open $%s(%s): %m", envname, e);
+ return dfd;
+ }
+
+ h = get_homedir();
+ if (!h) {
+ error("Unable to determine home directory");
+ return -1;
+ }
+
+ hfd = open(h, O_PATH | O_CLOEXEC | O_DIRECTORY);
+ if (hfd < 0) {
+ error("Unable to open $HOME(%s): %m", h);
+ return -1;
+ }
+
+ dfd = open_subdir(hfd, altpath, nofail);
+ close(hfd);
+ return dfd;
+}
+
/* FIXME: Can be shared */
void set_use_colors()
{
diff --git a/minecctl/misc.h b/minecctl/misc.h
index 3182ef8..d1c6816 100644
--- a/minecctl/misc.h
+++ b/minecctl/misc.h
@@ -2,6 +2,20 @@
#ifndef foomischfoo
#define foomischfoo
+int open_subdir(int dfd, const char *subdir, bool nofail);
+
+int open_xdg_dir(const char *envname, const char *altpath, bool nofail);
+
+static inline int open_xdg_shared_dir()
+{
+ return open_xdg_dir("XDG_DATA_HOME", ".local/share", false);
+}
+
+static inline int open_xdg_cfg_dir(bool nofail)
+{
+ return open_xdg_dir("XDG_CONFIG_HOME", ".config", nofail);
+}
+
void set_use_colors();
char **strv_copy(char *const *strv);
diff --git a/minecctl/rcon-commands.c b/minecctl/rcon-commands.c
index bab0004..44bb286 100644
--- a/minecctl/rcon-commands.c
+++ b/minecctl/rcon-commands.c
@@ -112,7 +112,7 @@ static int rcon_login(struct cfg *cfg, struct server *server)
fd = connect_any(&server->scfg.rcons, &saddr, &error);
if (fd < 0) {
- error("%s: unable to connect - %s", server->name, error);
+ verbose("%s: unable to connect - %s", server->name, error);
goto error;
} else
verbose("%s: connected to %s", server->name, saddr->addrstr);
@@ -344,24 +344,28 @@ static bool stop_one_server(struct cfg *cfg, struct server *server)
bool rv;
fd = rcon_login(cfg, server);
- if (fd < 0)
+ if (fd < 0) {
+ info("• %s: %sfail%s - unable to login",
+ server->name, ansi_red, ansi_normal);
return false;
+ }
- if (cfg->force_stop) {
+ if (!cfg->force_stop) {
unsigned current;
if (!get_player_count(fd, &current, NULL)) {
- error("%s: unable to get player count, not stopping",
- server->name);
+ info("• %s: %sfail%s - unable to get player count",
+ server->name, ansi_red, ansi_normal);
return false;
} else if (current > 0) {
- error("%s: has active players (use -f to force)",
- server->name);
+ info("• %s: %sfail%s - has active players "
+ "(use -f to force)",
+ server->name, ansi_red, ansi_normal);
return false;
}
}
- info("%s: sending stop command", server->name);
+ info("• %s: sending stop command", server->name);
rv = send_cmd(fd, "stop");
close(fd);
@@ -379,19 +383,12 @@ bool do_stop(struct cfg *cfg)
bool do_stop_all(struct cfg *cfg)
{
struct server *server;
- unsigned lineno;
- const char *error;
bool rv = true;
+ server_read_all_configs(cfg, false);
+
list_for_each_entry(server, &cfg->servers, list) {
- if (!server_read_config(cfg, server, &lineno, &error)) {
- info("• %s: %sfail%s - invalid configuration file "
- "(line %u: %s)",
- server->name, ansi_red, ansi_normal,
- lineno, error);
- rv = false;
- }
- /* FIXME: error checks */
+ /* FIXME: print more info, error checks */
stop_one_server(cfg, server);
}
diff --git a/minecctl/server.c b/minecctl/server.c
index 599be1f..91e7842 100644
--- a/minecctl/server.c
+++ b/minecctl/server.c
@@ -8,16 +8,81 @@
#include "minecctl.h"
#include "server.h"
#include "misc.h"
+#include "config.h"
#define INVALID(msg) do { *error = (msg); return false; } while(0)
+static DIR *open_cfg_dir(const char *cmdline_path)
+{
+ int dfd, mfd;
+ DIR *dir;
+
+ if (cmdline_path) {
+ dir = opendir(cmdline_path);
+ if (!dir)
+ error("opendir(%s): %m", cmdline_path);
+ return dir;
+ }
+
+ /* First, attempt per-user config dir... */
+ dfd = open_xdg_cfg_dir(false);
+ if (dfd >= 0) {
+ mfd = openat(dfd, "minecproxy/config",
+ O_CLOEXEC | O_DIRECTORY | O_RDONLY);
+ close(dfd);
+ if (mfd >= 0) {
+ dir = fdopendir(mfd);
+ if (!dir)
+ error("fdopendir: %m");
+ return dir;
+ }
+ }
+
+ /* ...and fallback on the system dir */
+ dir = opendir(DEFAULT_CFG_DIR);
+ if (!dir)
+ error("Failed to open configuration directory: %m");
+ return dir;
+}
+
+void server_load_all_known(struct cfg *cfg)
+{
+ struct dirent *dent;
+
+ if (cfg->server_list_loaded)
+ return;
+
+ cfg->server_list_loaded = true;
+ cfg->dir = open_cfg_dir(cfg->dir_path);
+ if (!cfg->dir) {
+ error("Failed to open config directory");
+ return;
+ }
+
+ while ((dent = readdir(cfg->dir))) {
+ struct server *server;
+ char *suffix;
+
+ if (!config_valid_server_filename(dent, NULL))
+ continue;
+
+ server = server_new(dent->d_name);
+
+ suffix = strrchr(dent->d_name, '.');
+ assert_die(suffix, "Error parsing filename");
+ *suffix = '\0';
+ server->name = xstrdup(dent->d_name);
+
+ list_add(&server->list, &cfg->servers);
+ }
+}
+
bool server_read_config(struct cfg *cfg, struct server *server,
unsigned *lineno, const char **error)
{
char buf[4096];
size_t off = 0;
ssize_t r;
- int dfd;
int fd;
if (!error)
@@ -35,19 +100,16 @@ bool server_read_config(struct cfg *cfg, struct server *server,
if (server->file_read)
return true;
+ if (!cfg->dir)
+ INVALID("Configuration directory not opened");
+
*lineno = 0;
server->file_read = true;
- dfd = open(cfg->cfgdir, O_DIRECTORY | O_PATH | O_CLOEXEC);
- if (dfd < 0)
- INVALID("failed to open configuration directory");
-
- fd = openat(dfd, server->scfg.filename, O_RDONLY | O_CLOEXEC);
+ fd = openat(dirfd(cfg->dir), server->scfg.filename, O_RDONLY | O_CLOEXEC);
if (fd < 0)
INVALID("failed to open configuration file");
- close(dfd);
-
while (true) {
r = read(fd, buf + off, sizeof(buf) - off - 1);
if (r < 0)
@@ -85,6 +147,8 @@ bool server_read_all_configs(struct cfg *cfg, bool print_results)
const char *error;
bool rv = true;
+ server_load_all_known(cfg);
+
/* server->scfg.filename check excludes servers created from cmdline */
list_for_each_entry(server, &cfg->servers, list) {
if (!server->scfg.filename)
@@ -111,6 +175,8 @@ bool server_read_all_configs(struct cfg *cfg, bool print_results)
ansi_normal);
}
}
+
+ return rv;
}
struct server *server_get_default(struct cfg *cfg)
@@ -124,7 +190,7 @@ struct server *server_get_default(struct cfg *cfg)
die("No servers defined");
if (!server_read_config(cfg, server, &lineno, &error)) {
- error("server_read_config error: %s", error);
+ error("%s: server_read_config error - %s", server->name, error);
return NULL;
}
@@ -137,6 +203,8 @@ bool server_set_default(struct cfg *cfg, const char *name)
assert_die(cfg, "invalid arguments");
+ server_load_all_known(cfg);
+
list_for_each_entry(server, &cfg->servers, list) {
if (streq(name, server->name)) {
list_rotate_to_front(&server->list, &cfg->servers);
@@ -148,37 +216,6 @@ bool server_set_default(struct cfg *cfg, const char *name)
return false;
}
-void server_load_all_known(struct cfg *cfg)
-{
- struct dirent *dent;
- DIR *dir;
-
- dir = opendir(cfg->cfgdir);
- if (!dir) {
- info("Can't open config directory %s: %m", cfg->cfgdir);
- return;
- }
-
- while ((dent = readdir(dir))) {
- struct server *server;
- char *suffix;
-
- if (!config_valid_server_filename(dent, NULL))
- continue;
-
- server = server_new(dent->d_name);
-
- suffix = strrchr(dent->d_name, '.');
- assert_die(suffix, "Error parsing filename");
- *suffix = '\0';
- server->name = xstrdup(dent->d_name);
-
- list_add(&server->list, &cfg->servers);
- }
-
- closedir(dir);
-}
-
void server_free_all(struct cfg *cfg)
{
struct server *server, *tmp;
diff --git a/minecctl/server.h b/minecctl/server.h
index 556187d..4520d34 100644
--- a/minecctl/server.h
+++ b/minecctl/server.h
@@ -13,6 +13,8 @@ struct server {
struct list_head list;
};
+void server_load_all_known(struct cfg *cfg);
+
bool server_read_config(struct cfg *cfg, struct server *server,
unsigned *lineno, const char **error);
@@ -22,8 +24,6 @@ struct server *server_get_default(struct cfg *cfg);
bool server_set_default(struct cfg *cfg, const char *name);
-void server_load_all_known(struct cfg *cfg);
-
void server_free_all(struct cfg *cfg);
void server_free(struct server *server);