summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Härdeman <david@hardeman.nu>2020-07-12 23:58:21 +0200
committerDavid Härdeman <david@hardeman.nu>2020-07-12 23:58:21 +0200
commit01eb1fe238371c3a29b22f1d06147f527285539e (patch)
treecc0301f776226dc12a109066c29a3a940c831a29
parent708f431db8dab6779dd090a1117e556340eca9cc (diff)
Teach minecctl to delete servers on commandHEADmaster
-rw-r--r--minecctl/minecctl-commands.h5
-rw-r--r--minecctl/minecctl.c18
-rw-r--r--minecctl/minecctl.h2
-rw-r--r--minecctl/misc-commands.c121
-rw-r--r--minecctl/misc-commands.h2
-rw-r--r--minecctl/rcon-commands.c2
-rw-r--r--shared/utils.h6
7 files changed, 151 insertions, 5 deletions
diff --git a/minecctl/minecctl-commands.h b/minecctl/minecctl-commands.h
index f50bc6c..b8a40a6 100644
--- a/minecctl/minecctl-commands.h
+++ b/minecctl/minecctl-commands.h
@@ -6,6 +6,7 @@ enum commands {
CMD_INVALID = 0,
CMD_INIT,
CMD_NEW,
+ CMD_DELETE,
CMD_LIST,
CMD_LINT,
CMD_INFO,
@@ -30,6 +31,10 @@ static struct command_list {
.cmd = CMD_NEW,
},
{
+ .name = "delete",
+ .cmd = CMD_DELETE,
+ },
+ {
.name = "list",
.cmd = CMD_LIST,
},
diff --git a/minecctl/minecctl.c b/minecctl/minecctl.c
index c2d6c19..51b106b 100644
--- a/minecctl/minecctl.c
+++ b/minecctl/minecctl.c
@@ -136,7 +136,7 @@ void dump_config(struct cfg *cfg)
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("│ force : %s", cfg->force ? "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");
@@ -158,6 +158,7 @@ _noreturn_ static void usage(bool no_error)
"Valid commands:\n"
" init perform initial setup\n"
" new SERVER create a new server\n"
+ " delete SERVER delete a server (-f to delete world as well)\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"
@@ -179,7 +180,7 @@ _noreturn_ static void usage(bool no_error)
" -m, --mc-address=ADDR connect to Minecraft server at ADDR\n"
" (only relevant for some commands, can also\n"
" use environment variable MC_ADDRESS)\n"
- " -f, --force stop server even if it has players\n"
+ " -f, --force force stop/delete actions\n"
" -v, --verbose enable extra logging\n"
" -d, --debug enable debugging information\n"
" -h, --help print this information\n"
@@ -335,6 +336,17 @@ static void parse_command(struct cfg *cfg, char *const *argv)
cfg->commands = strv_copy(argv);
cfg->cmd = do_new;
break;
+ case CMD_DELETE:
+ if (!*argv) {
+ error("Missing arguments");
+ usage(false);
+ } else if (*(argv + 1)) {
+ error("Too many arguments");
+ usage(false);
+ }
+ cfg->commands = strv_copy(argv);
+ cfg->cmd = do_delete;
+ break;
case CMD_LIST:
if (*argv) {
error("Too many arguments");
@@ -460,7 +472,7 @@ static void parse_cmdline(struct cfg *cfg, int argc, char *const *argv)
debug_mask = ~0;
break;
case 'f':
- cfg->force_stop = true;
+ cfg->force = true;
break;
case 'h':
usage(true);
diff --git a/minecctl/minecctl.h b/minecctl/minecctl.h
index 1d118d7..fd623df 100644
--- a/minecctl/minecctl.h
+++ b/minecctl/minecctl.h
@@ -18,7 +18,7 @@ struct cfg {
uint16_t mc_port_max;
uint16_t rcon_port_min;
uint16_t rcon_port_max;
- bool force_stop;
+ bool force;
bool default_set;
bool server_list_loaded;
bool (*cmd)(struct cfg *cfg);
diff --git a/minecctl/misc-commands.c b/minecctl/misc-commands.c
index 752b0e9..ff802d7 100644
--- a/minecctl/misc-commands.c
+++ b/minecctl/misc-commands.c
@@ -240,6 +240,9 @@ static bool valid_name(const char *name)
return false;
}
+ if (*f == '.')
+ return false;
+
while (*f != '\0') {
if ((*f >= 'a' && *f <= 'z') ||
(*f >= 'A' && *f <= 'Z') ||
@@ -543,6 +546,124 @@ bool do_new(struct cfg *cfg)
return true;
}
+static bool recursive_unlink(int dfd)
+{
+ _cleanup_closedir_ DIR *dir = NULL;
+ struct dirent *dent;
+ int sdfd;
+
+ dir = fdopendir(dfd);
+ if (!dir) {
+ error("fdopendir: %m");
+ close(dfd);
+ return false;
+ }
+
+ errno = 0;
+ while ((dent = readdir(dir))) {
+ if (streq(dent->d_name, ".") || streq(dent->d_name, ".."))
+ continue;
+
+ switch (dent->d_type) {
+ case DT_DIR:
+ sdfd = openat(dfd, dent->d_name, O_RDONLY | O_CLOEXEC |
+ O_DIRECTORY | O_NOCTTY | O_NOFOLLOW |
+ O_NOATIME);
+ if (sdfd < 0) {
+ error("Unable to open subdir %s: %m",
+ dent->d_name);
+ return false;
+ }
+
+ verbose("Checking subdir %s", dent->d_name);
+ if (!recursive_unlink(sdfd))
+ return false;
+
+ verbose("Deleting subdir %s", dent->d_name);
+ if (unlinkat(dfd, dent->d_name, AT_REMOVEDIR) != 0) {
+ error("Unable to unlink server subdir %s: %m",
+ dent->d_name);
+ return false;
+ }
+
+ break;
+
+ case DT_LNK:
+ _fallthrough_;
+ case DT_REG:
+ _fallthrough_;
+ case DT_UNKNOWN:
+ verbose("Unlinking file %s", dent->d_name);
+ if (unlinkat(dfd, dent->d_name, 0) != 0) {
+ error("Failed to unlink file: %s",
+ dent->d_name);
+ return false;
+ }
+
+ break;
+
+ default:
+ error("Unexpected file type in server directory: %s",
+ dent->d_name);
+ return false;
+ }
+ errno = 0;
+ }
+
+ if (errno != 0) {
+ error("readdir: %m");
+ return false;
+ }
+
+ return true;
+}
+
+bool do_delete(struct cfg *cfg)
+{
+ const char *name = cfg->commands[0];
+ char filename[strlen(name) + STRLEN(".") + STRLEN(SERVER_CONFIG_FILE_SUFFIX) + 1];
+ int dfd;
+
+ sprintf(filename, "%s.%s", name, SERVER_CONFIG_FILE_SUFFIX);
+
+ if (!valid_name(name))
+ return false;
+
+ server_load_all_known(cfg);
+
+ /* FIXME: Stop server first */
+
+ if (unlinkat(dirfd(cfg->cfg_dir), filename, 0) != 0) {
+ error("Unable to remove \"%s\": %m", filename);
+ if (!cfg->force)
+ return false;
+ } else {
+ verbose("Removed \"%s\"", filename);
+ if (!cfg->force)
+ return true;
+ }
+
+ dfd = openat(dirfd(cfg->data_dir), name, O_RDONLY | O_CLOEXEC |
+ O_DIRECTORY | O_NOCTTY | O_NOFOLLOW | O_NOATIME);
+ if (dfd < 0) {
+ error("Unable to open server directory: %m");
+ return false;
+ }
+
+ if (!recursive_unlink(dfd)) {
+ error("Failed to remove some file(s) in server directory");
+ return false;
+ }
+
+ verbose("Deleting server directory %s", name);
+ if (unlinkat(dirfd(cfg->data_dir), name, AT_REMOVEDIR)) {
+ error("Failed to remove server directory: %m");
+ return false;
+ }
+
+ return true;
+}
+
bool do_list(struct cfg *cfg)
{
struct server *server;
diff --git a/minecctl/misc-commands.h b/minecctl/misc-commands.h
index d8f62ca..9798da9 100644
--- a/minecctl/misc-commands.h
+++ b/minecctl/misc-commands.h
@@ -6,6 +6,8 @@ bool do_init(struct cfg *cfg);
bool do_new(struct cfg *cfg);
+bool do_delete(struct cfg *cfg);
+
bool do_list(struct cfg *cfg);
bool do_lint(struct cfg *cfg);
diff --git a/minecctl/rcon-commands.c b/minecctl/rcon-commands.c
index cdbf868..9719a01 100644
--- a/minecctl/rcon-commands.c
+++ b/minecctl/rcon-commands.c
@@ -340,7 +340,7 @@ static bool stop_one_server(struct cfg *cfg, struct server *server)
return false;
}
- if (!cfg->force_stop) {
+ if (!cfg->force) {
unsigned current;
if (!get_player_count(fd, &current, NULL)) {
diff --git a/shared/utils.h b/shared/utils.h
index b6bf51c..2bf7aae 100644
--- a/shared/utils.h
+++ b/shared/utils.h
@@ -188,4 +188,10 @@ static inline void fclosep(FILE **f) {
}
#define _cleanup_fclose_ _cleanup_(fclosep)
+static inline void closedirp(DIR **d) {
+ if (d && *d)
+ closedir(*d);
+}
+#define _cleanup_closedir_ _cleanup_(closedirp)
+
#endif