From a87e894ba3f3a8915389f651fb034f0d1835630c Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Fri, 26 Jun 2020 23:20:43 +0200 Subject: Split misc commands into a separate file --- minecctl/meson.build | 3 +- minecctl/minecctl-rcon.c | 470 ----------------------------------------------- minecctl/minecctl-rcon.h | 18 -- minecctl/minecctl.c | 16 +- minecctl/misc-commands.c | 18 ++ minecctl/misc-commands.h | 7 + minecctl/rcon-commands.c | 470 +++++++++++++++++++++++++++++++++++++++++++++++ minecctl/rcon-commands.h | 18 ++ 8 files changed, 517 insertions(+), 503 deletions(-) delete mode 100644 minecctl/minecctl-rcon.c delete mode 100644 minecctl/minecctl-rcon.h create mode 100644 minecctl/misc-commands.c create mode 100644 minecctl/misc-commands.h create mode 100644 minecctl/rcon-commands.c create mode 100644 minecctl/rcon-commands.h diff --git a/minecctl/meson.build b/minecctl/meson.build index a3a0813..e3bddcc 100644 --- a/minecctl/meson.build +++ b/minecctl/meson.build @@ -1,7 +1,8 @@ minecctl_sources = [ 'minecctl.c', - 'minecctl-rcon.c', 'server.c', + 'rcon-commands.c', + 'misc-commands.c', 'misc.c', ] diff --git a/minecctl/minecctl-rcon.c b/minecctl/minecctl-rcon.c deleted file mode 100644 index c387b38..0000000 --- a/minecctl/minecctl-rcon.c +++ /dev/null @@ -1,470 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "utils.h" -#include "minecctl.h" -#include "minecctl-rcon.h" -#include "server.h" -#include "rcon-protocol.h" -#include "misc.h" - -static void -send_packet(int sfd, const char *buf, size_t len) -{ - size_t off = 0; - ssize_t r; - - while (true) { - r = write(sfd, buf + off, len - off); - if (r < 0) { - if (errno == EINTR) - continue; - else - die("Failed to write packet: %m"); - } - - off += r; - if (off == len) - break; - } -} - -/* Note: msg is null-terminated due to the mc protocol trailer */ -static void -read_packet(int sfd, char *buf, size_t len, int32_t *id, int32_t *type, const char **msg) -{ - size_t off = 0; - ssize_t r; - const char *error; - - while (true) { - r = read(sfd, buf + off, len - off); - if (r < 0) { - if (errno == EINTR) - continue; - else - die("Failed to read reply: %m"); - } - - if (r == 0) - die("Failed, connection closed"); - - off += r; - if (rcon_protocol_packet_complete(buf, off)) - break; - - if (off >= len) - die("Reply too large %zu and %zu", off, len); - } - - if (!rcon_protocol_read_packet(buf, off, id, type, msg, &error)) - die("Failed to parse response: %s", error); -} - -static void -send_msg(int sfd, char *buf, size_t len, enum rcon_packet_type type, - const char *msg, enum rcon_packet_type *rtype, const char **reply) -{ - static uint32_t rcon_packet_id = 1; - size_t plen; - int32_t id; - - if (!rcon_protocol_create_packet(buf, len, &plen, - rcon_packet_id, type, msg)) - die("Failed to create rcon packet"); - - send_packet(sfd, buf, plen); - - read_packet(sfd, buf, len, &id, rtype, reply); - - /* FIXME: this should be shared */ - if (type == RCON_PACKET_LOGIN) { - if (*rtype != RCON_PACKET_LOGIN_OK) - die("Invalid reply id"); - - if (id == RCON_PACKET_LOGIN_FAIL) - *rtype = RCON_PACKET_LOGIN_FAIL; - else if (id != rcon_packet_id) - die("Invalid reply id"); - } else { - if (id != rcon_packet_id) - die("Invalid reply"); - } - - rcon_packet_id++; -} - -static int -rcon_login(struct cfg *cfg, struct server *server) -{ - char buf[4096]; - int32_t rtype; - const char *reply; - int fd = -1; - - assert_die(cfg && server, "invalid arguments"); - - if (list_empty(&server->rcon_addrs)) { - error("%s: rcon address unknown", server->name); - goto error; - } - - fd = connect_any(&server->rcon_addrs, true); - if (fd < 0) { - error("%s: unable to connect", server->name); - goto error; - } - - if (!server->rcon_password) - server->rcon_password = ask_password(); - - if (!server->rcon_password) { - error("%s: can't login - password missing", server->name); - goto error; - } - - send_msg(fd, buf, sizeof(buf), RCON_PACKET_LOGIN, server->rcon_password, - &rtype, &reply); - - explicit_bzero(buf, sizeof(buf)); - free_password(&server->rcon_password); - - if (rtype == RCON_PACKET_LOGIN_OK) - verbose("%s: login ok", server->name); - else if (rtype == RCON_PACKET_LOGIN_FAIL) { - info("%s: login failure, invalid password?", server->name); - goto error; - } else { - error("%s: invalid return code: %" PRIi32, server->name, rtype); - goto error; - } - - return fd; - -error: - close(fd); - return -1; -} - -static bool -send_cmd(int sfd, const char *cmd) -{ - char buf[4096]; - int32_t rtype; - const char *reply; - - send_msg(sfd, buf, sizeof(buf), RCON_PACKET_COMMAND, cmd, &rtype, &reply); - - if (rtype != RCON_PACKET_RESPONSE) { - die("Invalid return code: %" PRIi32, rtype); - return false; - } - - - if (use_colors) - info("%s%s%s", ANSI_GREY, reply, ANSI_NORMAL); - else - info("%s", reply); - - return true; -} - -static void -eat_whitespace(char **pos) -{ - char *end; - size_t len; - - while(isspace(**pos)) - (*pos)++; - - len = strlen(*pos); - if (len == 0) - return; - - end = *pos + len - 1; - while (isspace(*end)) - end--; - end++; - *end = '\0'; -} - -/* midnight = 18000 */ -#define MCTIME_OFFSET 6000 -#define MCTIME_PER_DAY 24000 -#define MCTIME_PER_HOUR 1000 -#define MIN_PER_HOUR 60 - -static inline unsigned -mctime_days(unsigned mctime) { - return (mctime / MCTIME_PER_DAY); -} - -static inline unsigned -mctime_hh(unsigned mctime) { - return (mctime % MCTIME_PER_DAY) / MCTIME_PER_HOUR; -} - -static inline unsigned -mctime_mm(unsigned mctime) { - return ((mctime % MCTIME_PER_HOUR) * MIN_PER_HOUR) / MCTIME_PER_HOUR; -} - -static bool -get_one_status(int fd, char *buf, size_t len, const char *cmd, - size_t argc, const char *replyscan, const char **reply, ...) -{ - int32_t rtype; - va_list ap; - int r; - - send_msg(fd, buf, len, RCON_PACKET_COMMAND, cmd, &rtype, reply); - if (rtype != RCON_PACKET_RESPONSE) - die("Invalid return code: %" PRIi32, rtype); - - va_start(ap, reply); - r = vsscanf(*reply, replyscan, ap); - va_end(ap); - - if (r == argc) - return true; - else - return false; -} - -bool -do_status(struct cfg *cfg) { - char buf[4096]; - char tbuf[4096]; - const char *reply; - unsigned cplayers, maxplayers, gtime; - unsigned epacks, apacks; - unsigned bannedplayers, bannedips; - int fd; - struct server *server; - - server = server_get_default(cfg); - fd = rcon_login(cfg, server); - if (fd < 0) - return false; - - if (get_one_status(fd, buf, sizeof(buf), "seed", 1, - "Seed : [ %[^]]]", &reply, tbuf)) - info("Seed: %s", tbuf); - - if (get_one_status(fd, buf, sizeof(buf), "difficulty", 1, - "The difficulty is %s", &reply, tbuf)) - info("Difficulty: %s", tbuf); - - if (get_one_status(fd, buf, sizeof(buf), "list", 2, - "There are %u of a max %u players online", - &reply, &cplayers, &maxplayers)) - info("Players: %u/%u", cplayers, maxplayers); - - if (get_one_status(fd, buf, sizeof(buf), "time query day", 1, - "The time is %u", &reply, >ime)) - info("In-game days: %u", gtime); - - if (get_one_status(fd, buf, sizeof(buf), "time query gametime", 1, - "The time is %u", &reply, >ime)) - info("World age: %ud:%02uh:%02um", - mctime_days(gtime), mctime_hh(gtime), mctime_mm(gtime)); - - if (get_one_status(fd, buf, sizeof(buf), "time query daytime", 1, - "The time is %u", &reply, >ime)) - info("Current in-game time: %02uh:%02um", - mctime_hh(gtime + MCTIME_OFFSET), mctime_mm(gtime + MCTIME_OFFSET)); - - if (get_one_status(fd, buf, sizeof(buf), "datapack list enabled", 2, - "There are %u data packs enabled: %[^\n]", &reply, &epacks, tbuf)) - info("Enabled data packs (%u): %s", epacks, tbuf); - - if (get_one_status(fd, buf, sizeof(buf), "datapack list available", 2, - "There are %u data packs available : %[^\n]", &reply, &apacks, tbuf)) - info("Available data packs (%u): %s", apacks, tbuf); - else if (streq(reply, "There are no more data packs available")) - info("Available data packs: none"); - - if (get_one_status(fd, buf, sizeof(buf), "banlist players", 1, - "There are %u bans", &reply, &bannedplayers)) - info("Banned players: %u", bannedplayers); - else if (streq(reply, "There are no bans")) - info("Banned players: 0"); - - if (get_one_status(fd, buf, sizeof(buf), "banlist ips", 1, - "There are %u bans", &reply, &bannedips)) - info("Banned IPs: %u", bannedips); - else if (streq(reply, "There are no bans")) - info("Banned IPs: 0"); - - return true; -} - -bool -do_ping(_unused_ struct cfg *cfg) { - die("Not implemented"); - return false; -} - -static bool -get_player_count(int fd, unsigned *current, unsigned *max) -{ - char buf[4096]; - const char *reply; - unsigned c, m; - - if (!get_one_status(fd, buf, sizeof(buf), "list", 2, - "There are %u of a max %u players online", - &reply, &c, &m)) - return false; - - if (current) - *current = c; - - if (max) - *max = m; - - return true; -} - -static bool -stop_one_server(struct cfg *cfg, struct server *server) -{ - int fd; - bool rv; - - fd = rcon_login(cfg, server); - if (fd < 0) - return false; - - if (cfg->force_stop) { - unsigned current; - - if (!get_player_count(fd, ¤t, NULL)) { - error("%s: unable to get player count, not stopping", - server->name); - return false; - } else if (current > 0) { - error("%s: has active players (use -f to force)", - server->name); - return false; - } - } - - info("%s: sending stop command", server->name); - rv = send_cmd(fd, "stop"); - close(fd); - - return rv; -} - -bool -do_stop(struct cfg *cfg) { - struct server *server; - - server = server_get_default(cfg); - return stop_one_server(cfg, server); -} - -bool -do_stop_all(struct cfg *cfg) { - struct server *server; - - list_for_each_entry(server, &cfg->servers, list) { - server_read_config(cfg, server); - stop_one_server(cfg, server); - } - - return true; -} - -bool -do_pcount(struct cfg *cfg) { - int fd; - unsigned current, max; - struct server *server; - - server = server_get_default(cfg); - fd = rcon_login(cfg, server); - if (fd < 0) - return false; - - if (get_player_count(fd, ¤t, &max)) { - info("Players: %u/%u", current, max); - return true; - } else { - die("Failed to get player count"); - return false; - } -} - -bool -do_console(struct cfg *cfg) -{ - char *prompt; - char *cmd; - int fd; - struct server *server; - - server = server_get_default(cfg); - fd = rcon_login(cfg, server); - if (fd < 0) - return false; - - prompt = alloca(strlen(program_invocation_short_name) + - STRLEN(" (") + strlen(server->name) + STRLEN("): ") + 1); - sprintf(prompt, "%s (%s): ", program_invocation_short_name, - server->name); - - while (true) { - char *tmp; - - cmd = readline(prompt); - if (!cmd) - break; - - tmp = cmd; - eat_whitespace(&tmp); - if (*tmp == '\0') { - xfree(cmd); - continue; - } - - if (streq(tmp, "q") || streq(tmp, "quit") || - streq(tmp, "/q") || streq(tmp, "/quit")) - break; - - send_cmd(fd, tmp); - - if (streq(tmp, "stop") || streq(tmp, "/stop")) - /* The server waits for us to close the connection */ - break; - - free(cmd); - } - - xfree(cmd); - return true; -} - -bool -do_command(struct cfg *cfg) { - int fd; - struct server *server; - - server = server_get_default(cfg); - fd = rcon_login(cfg, server); - if (fd < 0) - return false; - - return send_cmd(fd, cfg->cmdstr); -} - diff --git a/minecctl/minecctl-rcon.h b/minecctl/minecctl-rcon.h deleted file mode 100644 index 348845f..0000000 --- a/minecctl/minecctl-rcon.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef foominecctlrconhfoo -#define foominecctlrconhfoo - -bool do_status(struct cfg *cfg); - -bool do_ping(struct cfg *cfg); - -bool do_stop(struct cfg *cfg); - -bool do_stop_all(struct cfg *cfg); - -bool do_pcount(struct cfg *cfg); - -bool do_console(struct cfg *cfg); - -bool do_command(struct cfg *cfg); - -#endif diff --git a/minecctl/minecctl.c b/minecctl/minecctl.c index 95e3e35..b93f87b 100644 --- a/minecctl/minecctl.c +++ b/minecctl/minecctl.c @@ -15,8 +15,9 @@ #include "utils.h" #include "minecctl.h" #include "minecctl-commands.h" -#include "minecctl-rcon.h" #include "server.h" +#include "rcon-commands.h" +#include "misc-commands.h" #include "misc.h" #include "config-parser.h" #include "config.h" @@ -166,19 +167,6 @@ error: return false; } -static bool -do_list(struct cfg *cfg) -{ - struct server *server; - - /* server->filename check excludes servers created from cmdline */ - list_for_each_entry(server, &cfg->servers, list) - if (server->filename) - info("%s", server->name); - - return true; -} - static inline void get_optional_server_arg(struct cfg *cfg, char * const **argv, bool more) { diff --git a/minecctl/misc-commands.c b/minecctl/misc-commands.c new file mode 100644 index 0000000..6b70c55 --- /dev/null +++ b/minecctl/misc-commands.c @@ -0,0 +1,18 @@ +#include "utils.h" +#include "minecctl.h" +#include "server.h" +#include "misc-commands.h" + +bool +do_list(struct cfg *cfg) +{ + struct server *server; + + /* server->filename check excludes servers created from cmdline */ + list_for_each_entry(server, &cfg->servers, list) + if (server->filename) + info("%s", server->name); + + return true; +} + diff --git a/minecctl/misc-commands.h b/minecctl/misc-commands.h new file mode 100644 index 0000000..e0dc675 --- /dev/null +++ b/minecctl/misc-commands.h @@ -0,0 +1,7 @@ +#ifndef foomisccommandshfoo +#define foomisccommandshfoo + +bool do_list(struct cfg *cfg); + +#endif + diff --git a/minecctl/rcon-commands.c b/minecctl/rcon-commands.c new file mode 100644 index 0000000..02b970f --- /dev/null +++ b/minecctl/rcon-commands.c @@ -0,0 +1,470 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "minecctl.h" +#include "rcon-commands.h" +#include "server.h" +#include "rcon-protocol.h" +#include "misc.h" + +static void +send_packet(int sfd, const char *buf, size_t len) +{ + size_t off = 0; + ssize_t r; + + while (true) { + r = write(sfd, buf + off, len - off); + if (r < 0) { + if (errno == EINTR) + continue; + else + die("Failed to write packet: %m"); + } + + off += r; + if (off == len) + break; + } +} + +/* Note: msg is null-terminated due to the mc protocol trailer */ +static void +read_packet(int sfd, char *buf, size_t len, int32_t *id, int32_t *type, const char **msg) +{ + size_t off = 0; + ssize_t r; + const char *error; + + while (true) { + r = read(sfd, buf + off, len - off); + if (r < 0) { + if (errno == EINTR) + continue; + else + die("Failed to read reply: %m"); + } + + if (r == 0) + die("Failed, connection closed"); + + off += r; + if (rcon_protocol_packet_complete(buf, off)) + break; + + if (off >= len) + die("Reply too large %zu and %zu", off, len); + } + + if (!rcon_protocol_read_packet(buf, off, id, type, msg, &error)) + die("Failed to parse response: %s", error); +} + +static void +send_msg(int sfd, char *buf, size_t len, enum rcon_packet_type type, + const char *msg, enum rcon_packet_type *rtype, const char **reply) +{ + static uint32_t rcon_packet_id = 1; + size_t plen; + int32_t id; + + if (!rcon_protocol_create_packet(buf, len, &plen, + rcon_packet_id, type, msg)) + die("Failed to create rcon packet"); + + send_packet(sfd, buf, plen); + + read_packet(sfd, buf, len, &id, rtype, reply); + + /* FIXME: this should be shared */ + if (type == RCON_PACKET_LOGIN) { + if (*rtype != RCON_PACKET_LOGIN_OK) + die("Invalid reply id"); + + if (id == RCON_PACKET_LOGIN_FAIL) + *rtype = RCON_PACKET_LOGIN_FAIL; + else if (id != rcon_packet_id) + die("Invalid reply id"); + } else { + if (id != rcon_packet_id) + die("Invalid reply"); + } + + rcon_packet_id++; +} + +static int +rcon_login(struct cfg *cfg, struct server *server) +{ + char buf[4096]; + int32_t rtype; + const char *reply; + int fd = -1; + + assert_die(cfg && server, "invalid arguments"); + + if (list_empty(&server->rcon_addrs)) { + error("%s: rcon address unknown", server->name); + goto error; + } + + fd = connect_any(&server->rcon_addrs, true); + if (fd < 0) { + error("%s: unable to connect", server->name); + goto error; + } + + if (!server->rcon_password) + server->rcon_password = ask_password(); + + if (!server->rcon_password) { + error("%s: can't login - password missing", server->name); + goto error; + } + + send_msg(fd, buf, sizeof(buf), RCON_PACKET_LOGIN, server->rcon_password, + &rtype, &reply); + + explicit_bzero(buf, sizeof(buf)); + free_password(&server->rcon_password); + + if (rtype == RCON_PACKET_LOGIN_OK) + verbose("%s: login ok", server->name); + else if (rtype == RCON_PACKET_LOGIN_FAIL) { + info("%s: login failure, invalid password?", server->name); + goto error; + } else { + error("%s: invalid return code: %" PRIi32, server->name, rtype); + goto error; + } + + return fd; + +error: + close(fd); + return -1; +} + +static bool +send_cmd(int sfd, const char *cmd) +{ + char buf[4096]; + int32_t rtype; + const char *reply; + + send_msg(sfd, buf, sizeof(buf), RCON_PACKET_COMMAND, cmd, &rtype, &reply); + + if (rtype != RCON_PACKET_RESPONSE) { + die("Invalid return code: %" PRIi32, rtype); + return false; + } + + + if (use_colors) + info("%s%s%s", ANSI_GREY, reply, ANSI_NORMAL); + else + info("%s", reply); + + return true; +} + +static void +eat_whitespace(char **pos) +{ + char *end; + size_t len; + + while(isspace(**pos)) + (*pos)++; + + len = strlen(*pos); + if (len == 0) + return; + + end = *pos + len - 1; + while (isspace(*end)) + end--; + end++; + *end = '\0'; +} + +/* midnight = 18000 */ +#define MCTIME_OFFSET 6000 +#define MCTIME_PER_DAY 24000 +#define MCTIME_PER_HOUR 1000 +#define MIN_PER_HOUR 60 + +static inline unsigned +mctime_days(unsigned mctime) { + return (mctime / MCTIME_PER_DAY); +} + +static inline unsigned +mctime_hh(unsigned mctime) { + return (mctime % MCTIME_PER_DAY) / MCTIME_PER_HOUR; +} + +static inline unsigned +mctime_mm(unsigned mctime) { + return ((mctime % MCTIME_PER_HOUR) * MIN_PER_HOUR) / MCTIME_PER_HOUR; +} + +static bool +get_one_status(int fd, char *buf, size_t len, const char *cmd, + size_t argc, const char *replyscan, const char **reply, ...) +{ + int32_t rtype; + va_list ap; + int r; + + send_msg(fd, buf, len, RCON_PACKET_COMMAND, cmd, &rtype, reply); + if (rtype != RCON_PACKET_RESPONSE) + die("Invalid return code: %" PRIi32, rtype); + + va_start(ap, reply); + r = vsscanf(*reply, replyscan, ap); + va_end(ap); + + if (r == argc) + return true; + else + return false; +} + +bool +do_status(struct cfg *cfg) { + char buf[4096]; + char tbuf[4096]; + const char *reply; + unsigned cplayers, maxplayers, gtime; + unsigned epacks, apacks; + unsigned bannedplayers, bannedips; + int fd; + struct server *server; + + server = server_get_default(cfg); + fd = rcon_login(cfg, server); + if (fd < 0) + return false; + + if (get_one_status(fd, buf, sizeof(buf), "seed", 1, + "Seed : [ %[^]]]", &reply, tbuf)) + info("Seed: %s", tbuf); + + if (get_one_status(fd, buf, sizeof(buf), "difficulty", 1, + "The difficulty is %s", &reply, tbuf)) + info("Difficulty: %s", tbuf); + + if (get_one_status(fd, buf, sizeof(buf), "list", 2, + "There are %u of a max %u players online", + &reply, &cplayers, &maxplayers)) + info("Players: %u/%u", cplayers, maxplayers); + + if (get_one_status(fd, buf, sizeof(buf), "time query day", 1, + "The time is %u", &reply, >ime)) + info("In-game days: %u", gtime); + + if (get_one_status(fd, buf, sizeof(buf), "time query gametime", 1, + "The time is %u", &reply, >ime)) + info("World age: %ud:%02uh:%02um", + mctime_days(gtime), mctime_hh(gtime), mctime_mm(gtime)); + + if (get_one_status(fd, buf, sizeof(buf), "time query daytime", 1, + "The time is %u", &reply, >ime)) + info("Current in-game time: %02uh:%02um", + mctime_hh(gtime + MCTIME_OFFSET), mctime_mm(gtime + MCTIME_OFFSET)); + + if (get_one_status(fd, buf, sizeof(buf), "datapack list enabled", 2, + "There are %u data packs enabled: %[^\n]", &reply, &epacks, tbuf)) + info("Enabled data packs (%u): %s", epacks, tbuf); + + if (get_one_status(fd, buf, sizeof(buf), "datapack list available", 2, + "There are %u data packs available : %[^\n]", &reply, &apacks, tbuf)) + info("Available data packs (%u): %s", apacks, tbuf); + else if (streq(reply, "There are no more data packs available")) + info("Available data packs: none"); + + if (get_one_status(fd, buf, sizeof(buf), "banlist players", 1, + "There are %u bans", &reply, &bannedplayers)) + info("Banned players: %u", bannedplayers); + else if (streq(reply, "There are no bans")) + info("Banned players: 0"); + + if (get_one_status(fd, buf, sizeof(buf), "banlist ips", 1, + "There are %u bans", &reply, &bannedips)) + info("Banned IPs: %u", bannedips); + else if (streq(reply, "There are no bans")) + info("Banned IPs: 0"); + + return true; +} + +bool +do_ping(_unused_ struct cfg *cfg) { + die("Not implemented"); + return false; +} + +static bool +get_player_count(int fd, unsigned *current, unsigned *max) +{ + char buf[4096]; + const char *reply; + unsigned c, m; + + if (!get_one_status(fd, buf, sizeof(buf), "list", 2, + "There are %u of a max %u players online", + &reply, &c, &m)) + return false; + + if (current) + *current = c; + + if (max) + *max = m; + + return true; +} + +static bool +stop_one_server(struct cfg *cfg, struct server *server) +{ + int fd; + bool rv; + + fd = rcon_login(cfg, server); + if (fd < 0) + return false; + + if (cfg->force_stop) { + unsigned current; + + if (!get_player_count(fd, ¤t, NULL)) { + error("%s: unable to get player count, not stopping", + server->name); + return false; + } else if (current > 0) { + error("%s: has active players (use -f to force)", + server->name); + return false; + } + } + + info("%s: sending stop command", server->name); + rv = send_cmd(fd, "stop"); + close(fd); + + return rv; +} + +bool +do_stop(struct cfg *cfg) { + struct server *server; + + server = server_get_default(cfg); + return stop_one_server(cfg, server); +} + +bool +do_stop_all(struct cfg *cfg) { + struct server *server; + + list_for_each_entry(server, &cfg->servers, list) { + server_read_config(cfg, server); + stop_one_server(cfg, server); + } + + return true; +} + +bool +do_pcount(struct cfg *cfg) { + int fd; + unsigned current, max; + struct server *server; + + server = server_get_default(cfg); + fd = rcon_login(cfg, server); + if (fd < 0) + return false; + + if (get_player_count(fd, ¤t, &max)) { + info("Players: %u/%u", current, max); + return true; + } else { + die("Failed to get player count"); + return false; + } +} + +bool +do_console(struct cfg *cfg) +{ + char *prompt; + char *cmd; + int fd; + struct server *server; + + server = server_get_default(cfg); + fd = rcon_login(cfg, server); + if (fd < 0) + return false; + + prompt = alloca(strlen(program_invocation_short_name) + + STRLEN(" (") + strlen(server->name) + STRLEN("): ") + 1); + sprintf(prompt, "%s (%s): ", program_invocation_short_name, + server->name); + + while (true) { + char *tmp; + + cmd = readline(prompt); + if (!cmd) + break; + + tmp = cmd; + eat_whitespace(&tmp); + if (*tmp == '\0') { + xfree(cmd); + continue; + } + + if (streq(tmp, "q") || streq(tmp, "quit") || + streq(tmp, "/q") || streq(tmp, "/quit")) + break; + + send_cmd(fd, tmp); + + if (streq(tmp, "stop") || streq(tmp, "/stop")) + /* The server waits for us to close the connection */ + break; + + free(cmd); + } + + xfree(cmd); + return true; +} + +bool +do_command(struct cfg *cfg) { + int fd; + struct server *server; + + server = server_get_default(cfg); + fd = rcon_login(cfg, server); + if (fd < 0) + return false; + + return send_cmd(fd, cfg->cmdstr); +} + diff --git a/minecctl/rcon-commands.h b/minecctl/rcon-commands.h new file mode 100644 index 0000000..5366bf9 --- /dev/null +++ b/minecctl/rcon-commands.h @@ -0,0 +1,18 @@ +#ifndef foorconcommandshfoo +#define foorconcommandshfoo + +bool do_status(struct cfg *cfg); + +bool do_ping(struct cfg *cfg); + +bool do_stop(struct cfg *cfg); + +bool do_stop_all(struct cfg *cfg); + +bool do_pcount(struct cfg *cfg); + +bool do_console(struct cfg *cfg); + +bool do_command(struct cfg *cfg); + +#endif -- cgit v1.2.3